@muggleai/works 5.9.0 → 5.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 (171) hide show
  1. package/README.md +8 -0
  2. package/dist/{chunk-LVUJ4E62.js → chunk-MSPMXBO5.js} +638 -468
  3. package/dist/{chunk-5PYK7DWB.js → chunk-WUMAJOBT.js} +17 -6
  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 +19 -13
  9. package/dist/plugin/agents/test-prepare-runner.md +4 -1
  10. package/dist/plugin/agents/visual-walkthrough-builder.md +12 -5
  11. package/dist/plugin/commands/mtest.md +1 -1
  12. package/dist/plugin/hooks/README.md +15 -3
  13. package/dist/plugin/hooks/hooks.json +79 -0
  14. package/dist/plugin/scripts/ensure-electron-app.sh +62 -47
  15. package/dist/plugin/scripts/guardrail-classify-gate.sh +23 -0
  16. package/dist/plugin/scripts/guardrail-debug-path-gate.sh +41 -0
  17. package/dist/plugin/scripts/guardrail-pr-terminal.sh +6 -2
  18. package/dist/plugin/scripts/guardrail-record-stage-read.sh +21 -0
  19. package/dist/plugin/scripts/guardrail-record-stage-signals.sh +23 -0
  20. package/dist/plugin/scripts/guardrail-record-tests.sh +11 -4
  21. package/dist/plugin/scripts/guardrail-report-format.sh +9 -5
  22. package/dist/plugin/scripts/guardrail-skill-stages.sh +35 -0
  23. package/dist/plugin/scripts/guardrail-stage-gate.sh +43 -0
  24. package/dist/plugin/scripts/guardrail-walkthrough-gate.sh +41 -0
  25. package/dist/plugin/scripts/guardrails.mjs +474 -80
  26. package/dist/plugin/scripts/pr-watch-events.sh +116 -0
  27. package/dist/plugin/scripts/pr-watch-guards.sh +8 -2
  28. package/dist/plugin/scripts/pr-watch-loop.sh +254 -0
  29. package/dist/plugin/scripts/reconcile-stale-watchers.sh +66 -18
  30. package/dist/plugin/scripts/sign-body.sh +88 -0
  31. package/dist/plugin/skills/CLAUDE.md +16 -0
  32. package/dist/plugin/skills/_shared/post-merge-cleanup.md +83 -3
  33. package/dist/plugin/skills/_shared/pr-followup-helpers/loop-signature.md +6 -8
  34. package/dist/plugin/skills/_shared/pr-followup-helpers/reply-routing.md +10 -18
  35. package/dist/plugin/skills/_shared/resolve-e2e-validation-context.md +1 -0
  36. package/dist/plugin/skills/_shared/vcs/CLAUDE.md +1 -0
  37. package/dist/plugin/skills/_shared/vcs/github/pr-edit.md +3 -2
  38. package/dist/plugin/skills/_shared/vcs/github/reply-line-comment.md +3 -2
  39. package/dist/plugin/skills/_shared/vcs/github/top-level-comment.md +3 -2
  40. package/dist/plugin/skills/_shared/vcs/gitlab/mr-create.md +4 -1
  41. package/dist/plugin/skills/_shared/vcs/gitlab/mr-edit.md +4 -1
  42. package/dist/plugin/skills/_shared/vcs/gitlab/mr-note.md +4 -1
  43. package/dist/plugin/skills/_shared/vcs/gitlab/reply-discussion.md +4 -1
  44. package/dist/plugin/skills/_shared/vcs/post-signature.md +23 -12
  45. package/dist/plugin/skills/do/open-prs/forward.md +2 -2
  46. package/dist/plugin/skills/do/per-comment-replies.md +2 -15
  47. package/dist/plugin/skills/muggle/SKILL.md +1 -1
  48. package/dist/plugin/skills/muggle-browser-task/SKILL.md +1 -1
  49. package/dist/plugin/skills/muggle-pr-followup/CLAUDE.md +2 -1
  50. package/dist/plugin/skills/muggle-pr-followup/SKILL.md +6 -3
  51. package/dist/plugin/skills/muggle-pr-followup/adopt.md +60 -0
  52. package/dist/plugin/skills/muggle-pr-followup/arm-watcher.md +10 -14
  53. package/dist/plugin/skills/muggle-pr-followup/auto-track.md +2 -0
  54. package/dist/plugin/skills/muggle-pr-followup/evals/evals.json +63 -1
  55. package/dist/plugin/skills/muggle-pr-followup/output-templates/help.md +11 -1
  56. package/dist/plugin/skills/muggle-pr-followup/output-templates/inline-reply.md +1 -7
  57. package/dist/plugin/skills/muggle-pr-followup/output-templates/resolve-reminder.md +1 -4
  58. package/dist/plugin/skills/muggle-pr-followup/reconcile.md +32 -6
  59. package/dist/plugin/skills/muggle-pr-followup/state-schemas.md +18 -0
  60. package/dist/plugin/skills/muggle-preferences/SKILL.md +1 -1
  61. package/dist/plugin/skills/muggle-preferences/evals/evals.json +45 -0
  62. package/dist/plugin/skills/muggle-preferences/ops/change-one.md +1 -1
  63. package/dist/plugin/skills/muggle-preferences/ops/configure.md +4 -5
  64. package/dist/plugin/skills/muggle-preferences/ops/list.md +1 -1
  65. package/dist/plugin/skills/muggle-preferences/ops/set.md +3 -4
  66. package/dist/plugin/skills/muggle-preferences/preference-gates/README.md +3 -3
  67. package/dist/plugin/skills/muggle-preferences/preference-gates/autoSelectLocalHost.md +2 -2
  68. package/dist/plugin/skills/muggle-preferences/preference-gates/autoSelectProject.md +1 -1
  69. package/dist/plugin/skills/muggle-preferences/preference-gates/watcherLifetime.md +23 -0
  70. package/dist/plugin/skills/muggle-test/SKILL.md +23 -3
  71. package/dist/plugin/skills/muggle-test/execute-local.md +2 -0
  72. package/dist/plugin/skills/muggle-test-feature-local/SKILL.md +6 -1
  73. package/dist/plugin/skills/muggle-test-import/SKILL.md +1 -1
  74. package/dist/plugin/skills/muggle-test-prepare/SKILL.md +28 -15
  75. package/dist/plugin/skills/muggle-test-prepare/steps/check-running.md +1 -1
  76. package/dist/plugin/skills/muggle-test-prepare/steps/confirm-recipe.md +56 -0
  77. package/dist/plugin/skills/muggle-test-prepare/steps/derive-service-graph.md +51 -0
  78. package/dist/plugin/skills/muggle-test-prepare/steps/e2e-instructions.md +111 -0
  79. package/dist/plugin/skills/muggle-test-prepare/steps/identify-services.md +4 -2
  80. package/dist/plugin/skills/muggle-test-prepare/steps/readiness-report.md +12 -5
  81. package/dist/plugin/skills/muggle-test-prepare/steps/record-resolution.md +32 -0
  82. package/dist/plugin/skills/muggle-test-prepare/steps/replay-or-learn.md +20 -0
  83. package/dist/plugin/skills/muggle-test-prepare/steps/reuse-plan.md +9 -6
  84. package/dist/plugin/skills/muggle-test-prepare/steps/smoke-test.md +5 -1
  85. package/dist/plugin/skills/muggle-test-prepare/steps/start-services.md +3 -1
  86. package/dist/plugin/skills/muggle-test-regenerate-missing/SKILL.md +1 -1
  87. package/dist/release-manifest.json +4 -4
  88. package/dist/src-B5OTAZKM.js +1 -0
  89. package/package.json +7 -6
  90. package/plugin/.claude-plugin/plugin.json +1 -1
  91. package/plugin/.cursor-plugin/plugin.json +1 -1
  92. package/plugin/README.md +19 -13
  93. package/plugin/agents/test-prepare-runner.md +4 -1
  94. package/plugin/agents/visual-walkthrough-builder.md +12 -5
  95. package/plugin/commands/mtest.md +1 -1
  96. package/plugin/hooks/README.md +15 -3
  97. package/plugin/hooks/hooks.json +79 -0
  98. package/plugin/scripts/ensure-electron-app.sh +62 -47
  99. package/plugin/scripts/guardrail-classify-gate.sh +23 -0
  100. package/plugin/scripts/guardrail-debug-path-gate.sh +41 -0
  101. package/plugin/scripts/guardrail-pr-terminal.sh +6 -2
  102. package/plugin/scripts/guardrail-record-stage-read.sh +21 -0
  103. package/plugin/scripts/guardrail-record-stage-signals.sh +23 -0
  104. package/plugin/scripts/guardrail-record-tests.sh +11 -4
  105. package/plugin/scripts/guardrail-report-format.sh +9 -5
  106. package/plugin/scripts/guardrail-skill-stages.sh +35 -0
  107. package/plugin/scripts/guardrail-stage-gate.sh +43 -0
  108. package/plugin/scripts/guardrail-walkthrough-gate.sh +41 -0
  109. package/plugin/scripts/guardrails.mjs +474 -80
  110. package/plugin/scripts/pr-watch-events.sh +116 -0
  111. package/plugin/scripts/pr-watch-guards.sh +8 -2
  112. package/plugin/scripts/pr-watch-loop.sh +254 -0
  113. package/plugin/scripts/reconcile-stale-watchers.sh +66 -18
  114. package/plugin/scripts/sign-body.sh +88 -0
  115. package/plugin/skills/CLAUDE.md +16 -0
  116. package/plugin/skills/_shared/post-merge-cleanup.md +83 -3
  117. package/plugin/skills/_shared/pr-followup-helpers/loop-signature.md +6 -8
  118. package/plugin/skills/_shared/pr-followup-helpers/reply-routing.md +10 -18
  119. package/plugin/skills/_shared/resolve-e2e-validation-context.md +1 -0
  120. package/plugin/skills/_shared/vcs/CLAUDE.md +1 -0
  121. package/plugin/skills/_shared/vcs/github/pr-edit.md +3 -2
  122. package/plugin/skills/_shared/vcs/github/reply-line-comment.md +3 -2
  123. package/plugin/skills/_shared/vcs/github/top-level-comment.md +3 -2
  124. package/plugin/skills/_shared/vcs/gitlab/mr-create.md +4 -1
  125. package/plugin/skills/_shared/vcs/gitlab/mr-edit.md +4 -1
  126. package/plugin/skills/_shared/vcs/gitlab/mr-note.md +4 -1
  127. package/plugin/skills/_shared/vcs/gitlab/reply-discussion.md +4 -1
  128. package/plugin/skills/_shared/vcs/post-signature.md +23 -12
  129. package/plugin/skills/do/open-prs/forward.md +2 -2
  130. package/plugin/skills/do/per-comment-replies.md +2 -15
  131. package/plugin/skills/muggle/SKILL.md +1 -1
  132. package/plugin/skills/muggle-browser-task/SKILL.md +1 -1
  133. package/plugin/skills/muggle-pr-followup/CLAUDE.md +2 -1
  134. package/plugin/skills/muggle-pr-followup/SKILL.md +6 -3
  135. package/plugin/skills/muggle-pr-followup/adopt.md +60 -0
  136. package/plugin/skills/muggle-pr-followup/arm-watcher.md +10 -14
  137. package/plugin/skills/muggle-pr-followup/auto-track.md +2 -0
  138. package/plugin/skills/muggle-pr-followup/evals/evals.json +63 -1
  139. package/plugin/skills/muggle-pr-followup/output-templates/help.md +11 -1
  140. package/plugin/skills/muggle-pr-followup/output-templates/inline-reply.md +1 -7
  141. package/plugin/skills/muggle-pr-followup/output-templates/resolve-reminder.md +1 -4
  142. package/plugin/skills/muggle-pr-followup/reconcile.md +32 -6
  143. package/plugin/skills/muggle-pr-followup/state-schemas.md +18 -0
  144. package/plugin/skills/muggle-preferences/SKILL.md +1 -1
  145. package/plugin/skills/muggle-preferences/evals/evals.json +45 -0
  146. package/plugin/skills/muggle-preferences/ops/change-one.md +1 -1
  147. package/plugin/skills/muggle-preferences/ops/configure.md +4 -5
  148. package/plugin/skills/muggle-preferences/ops/list.md +1 -1
  149. package/plugin/skills/muggle-preferences/ops/set.md +3 -4
  150. package/plugin/skills/muggle-preferences/preference-gates/README.md +3 -3
  151. package/plugin/skills/muggle-preferences/preference-gates/autoSelectLocalHost.md +2 -2
  152. package/plugin/skills/muggle-preferences/preference-gates/autoSelectProject.md +1 -1
  153. package/plugin/skills/muggle-preferences/preference-gates/watcherLifetime.md +23 -0
  154. package/plugin/skills/muggle-test/SKILL.md +23 -3
  155. package/plugin/skills/muggle-test/execute-local.md +2 -0
  156. package/plugin/skills/muggle-test-feature-local/SKILL.md +6 -1
  157. package/plugin/skills/muggle-test-import/SKILL.md +1 -1
  158. package/plugin/skills/muggle-test-prepare/SKILL.md +28 -15
  159. package/plugin/skills/muggle-test-prepare/steps/check-running.md +1 -1
  160. package/plugin/skills/muggle-test-prepare/steps/confirm-recipe.md +56 -0
  161. package/plugin/skills/muggle-test-prepare/steps/derive-service-graph.md +51 -0
  162. package/plugin/skills/muggle-test-prepare/steps/e2e-instructions.md +111 -0
  163. package/plugin/skills/muggle-test-prepare/steps/identify-services.md +4 -2
  164. package/plugin/skills/muggle-test-prepare/steps/readiness-report.md +12 -5
  165. package/plugin/skills/muggle-test-prepare/steps/record-resolution.md +32 -0
  166. package/plugin/skills/muggle-test-prepare/steps/replay-or-learn.md +20 -0
  167. package/plugin/skills/muggle-test-prepare/steps/reuse-plan.md +9 -6
  168. package/plugin/skills/muggle-test-prepare/steps/smoke-test.md +5 -1
  169. package/plugin/skills/muggle-test-prepare/steps/start-services.md +3 -1
  170. package/plugin/skills/muggle-test-regenerate-missing/SKILL.md +1 -1
  171. package/dist/src-FDK2KQSZ.js +0 -1
@@ -2,7 +2,7 @@ import * as crypto from 'crypto';
2
2
  import { randomUUID } from 'crypto';
3
3
  import * as fs5 from 'fs';
4
4
  import { readFileSync, existsSync, mkdirSync, writeFileSync } from 'fs';
5
- import * as path9 from 'path';
5
+ import * as path10 from 'path';
6
6
  import { join, dirname } from 'path';
7
7
  import * as os4 from 'os';
8
8
  import { platform } from 'os';
@@ -10,7 +10,7 @@ import winston from 'winston';
10
10
  import { fileURLToPath } from 'url';
11
11
  import { exec, spawn } from 'child_process';
12
12
  import axios, { AxiosError } from 'axios';
13
- import * as fs8 from 'fs/promises';
13
+ import * as fs9 from 'fs/promises';
14
14
  import AppInsights from 'applicationinsights';
15
15
  import { z } from 'zod';
16
16
 
@@ -37,6 +37,9 @@ __export(src_exports, {
37
37
  PREFERENCE_ALLOWED_VALUES: () => PREFERENCE_ALLOWED_VALUES,
38
38
  PreferenceKey: () => PreferenceKey,
39
39
  PreferenceValue: () => PreferenceValue,
40
+ ProjectPreferencesReconcileOutcome: () => ProjectPreferencesReconcileOutcome,
41
+ WATCHER_LIFETIME_SECONDS: () => WATCHER_LIFETIME_SECONDS,
42
+ WATCHER_LIFETIME_UNBOUNDED_SECONDS: () => WATCHER_LIFETIME_UNBOUNDED_SECONDS,
40
43
  buildElectronAppChecksumsUrl: () => buildElectronAppChecksumsUrl,
41
44
  buildElectronAppReleaseAssetUrl: () => buildElectronAppReleaseAssetUrl,
42
45
  buildElectronAppReleaseTag: () => buildElectronAppReleaseTag,
@@ -80,8 +83,7 @@ __export(src_exports, {
80
83
  performLogout: () => performLogout,
81
84
  pollDeviceCode: () => pollDeviceCode,
82
85
  qa: () => e2e_exports2,
83
- readGlobalPreferences: () => readGlobalPreferences,
84
- readProjectPreferences: () => readProjectPreferences,
86
+ reconcileProjectPreferences: () => reconcileProjectPreferences,
85
87
  resetConfig: () => resetConfig,
86
88
  resetLogger: () => resetLogger,
87
89
  resetPreference: () => resetPreference,
@@ -98,7 +100,7 @@ __export(src_exports, {
98
100
  });
99
101
  var DATA_DIR_NAME = ".muggle-ai";
100
102
  function getDataDir() {
101
- return path9.join(os4.homedir(), DATA_DIR_NAME);
103
+ return path10.join(os4.homedir(), DATA_DIR_NAME);
102
104
  }
103
105
  var DEV_MANIFEST = {
104
106
  release: "dev",
@@ -162,24 +164,24 @@ var configInstance = null;
162
164
  var muggleConfigCache = null;
163
165
  function getPackageRoot() {
164
166
  const currentFilePath = fileURLToPath(import.meta.url);
165
- const currentDir = path9.dirname(currentFilePath);
166
- if (currentDir.includes(path9.join("dist", "shared"))) {
167
- return path9.resolve(currentDir, "..", "..");
167
+ const currentDir = path10.dirname(currentFilePath);
168
+ if (currentDir.includes(path10.join("dist", "shared"))) {
169
+ return path10.resolve(currentDir, "..", "..");
168
170
  }
169
171
  if (currentDir.endsWith("dist")) {
170
- return path9.resolve(currentDir, "..");
172
+ return path10.resolve(currentDir, "..");
171
173
  }
172
- if (currentDir.includes(path9.join("src", "shared"))) {
173
- return path9.resolve(currentDir, "..", "..");
174
+ if (currentDir.includes(path10.join("src", "shared"))) {
175
+ return path10.resolve(currentDir, "..", "..");
174
176
  }
175
- return path9.dirname(currentDir);
177
+ return path10.dirname(currentDir);
176
178
  }
177
179
  function getMuggleConfig() {
178
180
  if (muggleConfigCache) {
179
181
  return muggleConfigCache;
180
182
  }
181
183
  const packageRoot = getPackageRoot();
182
- const packageJsonPath = path9.join(packageRoot, "package.json");
184
+ const packageJsonPath = path10.join(packageRoot, "package.json");
183
185
  let packageJson;
184
186
  try {
185
187
  const content = fs5.readFileSync(packageJsonPath, "utf-8");
@@ -242,17 +244,17 @@ function getDataDir2() {
242
244
  function getDownloadedElectronAppPath() {
243
245
  const platformName = os4.platform();
244
246
  const version = getElectronAppVersion();
245
- const baseDir = path9.join(getDataDir2(), ELECTRON_APP_DIR, version);
247
+ const baseDir = path10.join(getDataDir2(), ELECTRON_APP_DIR, version);
246
248
  let binaryPath;
247
249
  switch (platformName) {
248
250
  case "darwin":
249
- binaryPath = path9.join(baseDir, "MuggleAI.app", "Contents", "MacOS", "MuggleAI");
251
+ binaryPath = path10.join(baseDir, "MuggleAI.app", "Contents", "MacOS", "MuggleAI");
250
252
  break;
251
253
  case "win32":
252
- binaryPath = path9.join(baseDir, "MuggleAI.exe");
254
+ binaryPath = path10.join(baseDir, "MuggleAI.exe");
253
255
  break;
254
256
  case "linux":
255
- binaryPath = path9.join(baseDir, "MuggleAI");
257
+ binaryPath = path10.join(baseDir, "MuggleAI");
256
258
  break;
257
259
  default:
258
260
  return null;
@@ -268,13 +270,13 @@ function getSystemElectronAppPath() {
268
270
  let binaryPath;
269
271
  switch (platformName) {
270
272
  case "darwin":
271
- binaryPath = path9.join(homeDir, "Applications", "MuggleAI.app", "Contents", "MacOS", "MuggleAI");
273
+ binaryPath = path10.join(homeDir, "Applications", "MuggleAI.app", "Contents", "MacOS", "MuggleAI");
272
274
  break;
273
275
  case "win32":
274
- binaryPath = path9.join(homeDir, "AppData", "Local", "Programs", "MuggleAI", "MuggleAI.exe");
276
+ binaryPath = path10.join(homeDir, "AppData", "Local", "Programs", "MuggleAI", "MuggleAI.exe");
275
277
  break;
276
278
  case "linux":
277
- binaryPath = path9.join(homeDir, ".local", "share", "muggle-ai", "MuggleAI");
279
+ binaryPath = path10.join(homeDir, ".local", "share", "muggle-ai", "MuggleAI");
278
280
  break;
279
281
  default:
280
282
  return null;
@@ -305,7 +307,7 @@ function resolveWebServicePath() {
305
307
  return customPath;
306
308
  }
307
309
  const packageRoot = getPackageRoot();
308
- const siblingPath = path9.resolve(packageRoot, "..", "web-service", "dist", "src", "index.js");
310
+ const siblingPath = path10.resolve(packageRoot, "..", "web-service", "dist", "src", "index.js");
309
311
  if (fs5.existsSync(siblingPath)) {
310
312
  return siblingPath;
311
313
  }
@@ -392,13 +394,13 @@ function buildLocalQaConfig() {
392
394
  webServiceUrl: process.env.WEB_SERVICE_URL ?? DEFAULT_WEB_SERVICE_URL,
393
395
  promptServiceUrl: process.env.PROMPT_SERVICE_BASE_URL ?? defaultPromptServiceUrl,
394
396
  dataDir,
395
- sessionsDir: path9.join(dataDir, "sessions"),
396
- projectsDir: path9.join(dataDir, "projects"),
397
- tempDir: path9.join(dataDir, "temp"),
398
- apiKeyFilePath: path9.join(dataDir, API_KEY_FILE),
399
- oauthSessionFilePath: path9.join(dataDir, "oauth-session.json"),
397
+ sessionsDir: path10.join(dataDir, "sessions"),
398
+ projectsDir: path10.join(dataDir, "projects"),
399
+ tempDir: path10.join(dataDir, "temp"),
400
+ apiKeyFilePath: path10.join(dataDir, API_KEY_FILE),
401
+ oauthSessionFilePath: path10.join(dataDir, "oauth-session.json"),
400
402
  webServicePath: resolveWebServicePath(),
401
- webServicePidFile: path9.join(dataDir, "web-service.pid"),
403
+ webServicePidFile: path10.join(dataDir, "web-service.pid"),
402
404
  auth0: {
403
405
  domain: process.env.AUTH0_DOMAIN ?? defaultAuth0Domain,
404
406
  clientId: process.env.AUTH0_CLIENT_ID ?? defaultAuth0ClientId,
@@ -433,7 +435,7 @@ function getElectronAppVersion() {
433
435
  if (envVersion && /^\d+\.\d+\.\d+$/.test(envVersion)) {
434
436
  return envVersion;
435
437
  }
436
- const overridePath = path9.join(getDataDir2(), VERSION_OVERRIDE_FILE);
438
+ const overridePath = path10.join(getDataDir2(), VERSION_OVERRIDE_FILE);
437
439
  if (fs5.existsSync(overridePath)) {
438
440
  try {
439
441
  const content = JSON.parse(fs5.readFileSync(overridePath, "utf-8"));
@@ -450,7 +452,7 @@ function getElectronAppVersionSource() {
450
452
  if (envVersion && /^\d+\.\d+\.\d+$/.test(envVersion)) {
451
453
  return "env";
452
454
  }
453
- const overridePath = path9.join(getDataDir2(), VERSION_OVERRIDE_FILE);
455
+ const overridePath = path10.join(getDataDir2(), VERSION_OVERRIDE_FILE);
454
456
  if (fs5.existsSync(overridePath)) {
455
457
  try {
456
458
  const content = JSON.parse(fs5.readFileSync(overridePath, "utf-8"));
@@ -485,7 +487,7 @@ function isElectronAppInstalled() {
485
487
  }
486
488
  function getElectronAppDir(version) {
487
489
  const resolvedVersion = version ?? getElectronAppVersion();
488
- return path9.join(getDataDir2(), ELECTRON_APP_DIR, resolvedVersion);
490
+ return path10.join(getDataDir2(), ELECTRON_APP_DIR, resolvedVersion);
489
491
  }
490
492
 
491
493
  // packages/mcps/src/shared/logger.ts
@@ -542,14 +544,14 @@ function getPlatformKey() {
542
544
  }
543
545
  }
544
546
  async function calculateFileChecksum(filePath) {
545
- return new Promise((resolve4, reject) => {
547
+ return new Promise((resolve5, reject) => {
546
548
  const hash = crypto.createHash("sha256");
547
549
  const stream = fs5.createReadStream(filePath);
548
550
  stream.on("data", (data) => {
549
551
  hash.update(data);
550
552
  });
551
553
  stream.on("end", () => {
552
- resolve4(hash.digest("hex"));
554
+ resolve5(hash.digest("hex"));
553
555
  });
554
556
  stream.on("error", (error) => {
555
557
  reject(error);
@@ -559,7 +561,7 @@ async function calculateFileChecksum(filePath) {
559
561
  async function verifyFileChecksum(filePath, expectedChecksum) {
560
562
  if (!expectedChecksum || expectedChecksum.trim() === "") {
561
563
  logger.warn("Checksum verification skipped - no checksum provided", {
562
- file: path9.basename(filePath)
564
+ file: path10.basename(filePath)
563
565
  });
564
566
  return {
565
567
  valid: true,
@@ -575,13 +577,13 @@ async function verifyFileChecksum(filePath, expectedChecksum) {
575
577
  const valid = normalizedExpected === normalizedActual;
576
578
  if (!valid) {
577
579
  logger.error("Checksum verification failed", {
578
- file: path9.basename(filePath),
580
+ file: path10.basename(filePath),
579
581
  expected: normalizedExpected,
580
582
  actual: normalizedActual
581
583
  });
582
584
  } else {
583
585
  logger.info("Checksum verified successfully", {
584
- file: path9.basename(filePath),
586
+ file: path10.basename(filePath),
585
587
  checksum: normalizedActual
586
588
  });
587
589
  }
@@ -594,7 +596,7 @@ async function verifyFileChecksum(filePath, expectedChecksum) {
594
596
  } catch (error) {
595
597
  const errorMessage = error instanceof Error ? error.message : String(error);
596
598
  logger.error("Checksum calculation failed", {
597
- file: path9.basename(filePath),
599
+ file: path10.basename(filePath),
598
600
  error: errorMessage
599
601
  });
600
602
  return {
@@ -627,7 +629,7 @@ function getOpenCommand(url) {
627
629
  }
628
630
  }
629
631
  async function openBrowserUrl(options) {
630
- return new Promise((resolve4) => {
632
+ return new Promise((resolve5) => {
631
633
  try {
632
634
  const command = getOpenCommand(options.url);
633
635
  logger2.debug("[Browser] Opening URL", { url: options.url, command });
@@ -637,13 +639,13 @@ async function openBrowserUrl(options) {
637
639
  url: options.url,
638
640
  error: error.message
639
641
  });
640
- resolve4({
642
+ resolve5({
641
643
  opened: false,
642
644
  error: error.message
643
645
  });
644
646
  } else {
645
647
  logger2.info("[Browser] URL opened successfully", { url: options.url });
646
- resolve4({ opened: true });
648
+ resolve5({ opened: true });
647
649
  }
648
650
  });
649
651
  } catch (error) {
@@ -652,7 +654,7 @@ async function openBrowserUrl(options) {
652
654
  url: options.url,
653
655
  error: errorMessage
654
656
  });
655
- resolve4({
657
+ resolve5({
656
658
  opened: false,
657
659
  error: errorMessage
658
660
  });
@@ -661,7 +663,7 @@ async function openBrowserUrl(options) {
661
663
  }
662
664
  var API_KEY_FILE2 = "api-key.json";
663
665
  function getApiKeyFilePath() {
664
- return path9.join(getDataDir(), API_KEY_FILE2);
666
+ return path10.join(getDataDir(), API_KEY_FILE2);
665
667
  }
666
668
  function ensureDataDir() {
667
669
  const dataDir = getDataDir();
@@ -850,8 +852,8 @@ var AuthService = class {
850
852
  constructor() {
851
853
  const config = getConfig();
852
854
  this.oauthSessionFilePath = config.localQa.oauthSessionFilePath;
853
- this.pendingDeviceCodePath = path9.join(
854
- path9.dirname(config.localQa.oauthSessionFilePath),
855
+ this.pendingDeviceCodePath = path10.join(
856
+ path10.dirname(config.localQa.oauthSessionFilePath),
855
857
  "pending-device-code.json"
856
858
  );
857
859
  }
@@ -960,7 +962,7 @@ var AuthService = class {
960
962
  */
961
963
  storePendingDeviceCode(params) {
962
964
  const logger6 = getLogger();
963
- const dir = path9.dirname(this.pendingDeviceCodePath);
965
+ const dir = path10.dirname(this.pendingDeviceCodePath);
964
966
  if (!fs5.existsSync(dir)) {
965
967
  fs5.mkdirSync(dir, { recursive: true });
966
968
  }
@@ -1173,7 +1175,7 @@ var AuthService = class {
1173
1175
  email,
1174
1176
  userId
1175
1177
  };
1176
- const dir = path9.dirname(this.oauthSessionFilePath);
1178
+ const dir = path10.dirname(this.oauthSessionFilePath);
1177
1179
  if (!fs5.existsSync(dir)) {
1178
1180
  fs5.mkdirSync(dir, { recursive: true });
1179
1181
  }
@@ -1276,7 +1278,7 @@ var AuthService = class {
1276
1278
  email: storedAuth.email,
1277
1279
  userId: storedAuth.userId
1278
1280
  };
1279
- const dir = path9.dirname(this.oauthSessionFilePath);
1281
+ const dir = path10.dirname(this.oauthSessionFilePath);
1280
1282
  if (!fs5.existsSync(dir)) {
1281
1283
  fs5.mkdirSync(dir, { recursive: true });
1282
1284
  }
@@ -1357,9 +1359,9 @@ function resetAuthService() {
1357
1359
  serviceInstance = null;
1358
1360
  }
1359
1361
  function sleep(params) {
1360
- return new Promise((resolve4) => {
1362
+ return new Promise((resolve5) => {
1361
1363
  setTimeout(() => {
1362
- resolve4();
1364
+ resolve5();
1363
1365
  }, params.durationMs);
1364
1366
  });
1365
1367
  }
@@ -1399,11 +1401,11 @@ var StorageService = class {
1399
1401
  createSessionDirectory(sessionId) {
1400
1402
  const logger6 = getLogger();
1401
1403
  this.ensureDirectories();
1402
- const sessionDir = path9.join(this.sessionsDir, sessionId);
1404
+ const sessionDir = path10.join(this.sessionsDir, sessionId);
1403
1405
  if (!fs5.existsSync(sessionDir)) {
1404
1406
  fs5.mkdirSync(sessionDir, { recursive: true });
1405
- fs5.mkdirSync(path9.join(sessionDir, "screenshots"), { recursive: true });
1406
- fs5.mkdirSync(path9.join(sessionDir, "logs"), { recursive: true });
1407
+ fs5.mkdirSync(path10.join(sessionDir, "screenshots"), { recursive: true });
1408
+ fs5.mkdirSync(path10.join(sessionDir, "logs"), { recursive: true });
1407
1409
  logger6.info("Created session directory", { sessionId, path: sessionDir });
1408
1410
  }
1409
1411
  return sessionDir;
@@ -1415,7 +1417,7 @@ var StorageService = class {
1415
1417
  saveSessionMetadata(metadata) {
1416
1418
  const logger6 = getLogger();
1417
1419
  const sessionDir = this.createSessionDirectory(metadata.sessionId);
1418
- const metadataPath = path9.join(sessionDir, "metadata.json");
1420
+ const metadataPath = path10.join(sessionDir, "metadata.json");
1419
1421
  fs5.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2), "utf-8");
1420
1422
  logger6.debug("Saved session metadata", { sessionId: metadata.sessionId });
1421
1423
  }
@@ -1426,7 +1428,7 @@ var StorageService = class {
1426
1428
  */
1427
1429
  loadSessionMetadata(sessionId) {
1428
1430
  const logger6 = getLogger();
1429
- const metadataPath = path9.join(this.sessionsDir, sessionId, "metadata.json");
1431
+ const metadataPath = path10.join(this.sessionsDir, sessionId, "metadata.json");
1430
1432
  if (!fs5.existsSync(metadataPath)) {
1431
1433
  return null;
1432
1434
  }
@@ -1450,7 +1452,7 @@ var StorageService = class {
1450
1452
  return [];
1451
1453
  }
1452
1454
  return fs5.readdirSync(this.sessionsDir).filter((entry) => {
1453
- const entryPath = path9.join(this.sessionsDir, entry);
1455
+ const entryPath = path10.join(this.sessionsDir, entry);
1454
1456
  return fs5.statSync(entryPath).isDirectory();
1455
1457
  });
1456
1458
  }
@@ -1459,13 +1461,13 @@ var StorageService = class {
1459
1461
  * @returns Current session ID, or null.
1460
1462
  */
1461
1463
  getCurrentSessionId() {
1462
- const currentPath = path9.join(this.sessionsDir, "current");
1464
+ const currentPath = path10.join(this.sessionsDir, "current");
1463
1465
  if (!fs5.existsSync(currentPath)) {
1464
1466
  return null;
1465
1467
  }
1466
1468
  try {
1467
1469
  const target = fs5.readlinkSync(currentPath);
1468
- return path9.basename(target);
1470
+ return path10.basename(target);
1469
1471
  } catch {
1470
1472
  return null;
1471
1473
  }
@@ -1476,8 +1478,8 @@ var StorageService = class {
1476
1478
  */
1477
1479
  setCurrentSession(sessionId) {
1478
1480
  const logger6 = getLogger();
1479
- const currentPath = path9.join(this.sessionsDir, "current");
1480
- const targetPath = path9.join(this.sessionsDir, sessionId);
1481
+ const currentPath = path10.join(this.sessionsDir, "current");
1482
+ const targetPath = path10.join(this.sessionsDir, sessionId);
1481
1483
  if (fs5.existsSync(currentPath)) {
1482
1484
  fs5.unlinkSync(currentPath);
1483
1485
  }
@@ -1492,7 +1494,7 @@ var StorageService = class {
1492
1494
  const { sessionId, filename, data } = params;
1493
1495
  const logger6 = getLogger();
1494
1496
  const sessionDir = this.createSessionDirectory(sessionId);
1495
- const screenshotPath = path9.join(sessionDir, "screenshots", filename);
1497
+ const screenshotPath = path10.join(sessionDir, "screenshots", filename);
1496
1498
  fs5.writeFileSync(screenshotPath, data);
1497
1499
  logger6.debug("Saved screenshot", { sessionId, filename });
1498
1500
  return screenshotPath;
@@ -1505,7 +1507,7 @@ var StorageService = class {
1505
1507
  const { sessionId, content } = params;
1506
1508
  const logger6 = getLogger();
1507
1509
  const sessionDir = this.createSessionDirectory(sessionId);
1508
- const resultsPath = path9.join(sessionDir, "results.md");
1510
+ const resultsPath = path10.join(sessionDir, "results.md");
1509
1511
  fs5.appendFileSync(resultsPath, content + "\n", "utf-8");
1510
1512
  logger6.debug("Appended to results", { sessionId });
1511
1513
  }
@@ -1515,7 +1517,7 @@ var StorageService = class {
1515
1517
  * @returns Results content, or null if not found.
1516
1518
  */
1517
1519
  getResults(sessionId) {
1518
- const resultsPath = path9.join(this.sessionsDir, sessionId, "results.md");
1520
+ const resultsPath = path10.join(this.sessionsDir, sessionId, "results.md");
1519
1521
  if (!fs5.existsSync(resultsPath)) {
1520
1522
  return null;
1521
1523
  }
@@ -1584,7 +1586,7 @@ var StorageService = class {
1584
1586
  const { sessionId, targetUrl, testInstructions } = params;
1585
1587
  const logger6 = getLogger();
1586
1588
  const sessionDir = this.createSessionDirectory(sessionId);
1587
- const resultsPath = path9.join(sessionDir, "results.md");
1589
+ const resultsPath = path10.join(sessionDir, "results.md");
1588
1590
  const header = [
1589
1591
  `# Test Results: ${sessionId}`,
1590
1592
  "",
@@ -1608,7 +1610,7 @@ var StorageService = class {
1608
1610
  const { sessionId, step } = params;
1609
1611
  const logger6 = getLogger();
1610
1612
  const sessionDir = this.createSessionDirectory(sessionId);
1611
- const resultsPath = path9.join(sessionDir, "results.md");
1613
+ const resultsPath = path10.join(sessionDir, "results.md");
1612
1614
  const statusIcon = step.success ? "\u2713" : "\u2717";
1613
1615
  const stepContent = [
1614
1616
  `### Step ${step.stepNumber}: ${step.action}`,
@@ -1633,8 +1635,8 @@ var StorageService = class {
1633
1635
  finalizeResults(params) {
1634
1636
  const { sessionId, status, summary } = params;
1635
1637
  const logger6 = getLogger();
1636
- const sessionDir = path9.join(this.sessionsDir, sessionId);
1637
- const resultsPath = path9.join(sessionDir, "results.md");
1638
+ const sessionDir = path10.join(this.sessionsDir, sessionId);
1639
+ const resultsPath = path10.join(sessionDir, "results.md");
1638
1640
  if (!fs5.existsSync(resultsPath)) {
1639
1641
  logger6.warn("Results file not found for finalization", { sessionId });
1640
1642
  return;
@@ -1716,7 +1718,7 @@ var StorageService = class {
1716
1718
  }
1717
1719
  const sessionDate = new Date(metadata.startTime);
1718
1720
  if (sessionDate < cutoffDate) {
1719
- const sessionDir = path9.join(this.sessionsDir, sessionId);
1721
+ const sessionDir = path10.join(this.sessionsDir, sessionId);
1720
1722
  try {
1721
1723
  fs5.rmSync(sessionDir, { recursive: true, force: true });
1722
1724
  deletedCount++;
@@ -1746,7 +1748,7 @@ var StorageService = class {
1746
1748
  * @returns Session directory path.
1747
1749
  */
1748
1750
  getSessionPath(sessionId) {
1749
- return path9.join(this.sessionsDir, sessionId);
1751
+ return path10.join(this.sessionsDir, sessionId);
1750
1752
  }
1751
1753
  /**
1752
1754
  * Delete a specific session.
@@ -1755,7 +1757,7 @@ var StorageService = class {
1755
1757
  */
1756
1758
  deleteSession(sessionId) {
1757
1759
  const logger6 = getLogger();
1758
- const sessionDir = path9.join(this.sessionsDir, sessionId);
1760
+ const sessionDir = path10.join(this.sessionsDir, sessionId);
1759
1761
  if (!fs5.existsSync(sessionDir)) {
1760
1762
  logger6.warn("Session not found for deletion", { sessionId });
1761
1763
  return false;
@@ -1763,7 +1765,7 @@ var StorageService = class {
1763
1765
  try {
1764
1766
  const currentId = this.getCurrentSessionId();
1765
1767
  if (currentId === sessionId) {
1766
- const currentPath = path9.join(this.sessionsDir, "current");
1768
+ const currentPath = path10.join(this.sessionsDir, "current");
1767
1769
  if (fs5.existsSync(currentPath)) {
1768
1770
  fs5.unlinkSync(currentPath);
1769
1771
  }
@@ -1797,8 +1799,8 @@ var RunResultStorageService = class {
1797
1799
  constructor() {
1798
1800
  const storageService = getStorageService();
1799
1801
  const dataDir = storageService.getDataDir();
1800
- this.runResultsDir = path9.join(dataDir, "run-results");
1801
- this.testScriptsDir = path9.join(dataDir, "test-scripts");
1802
+ this.runResultsDir = path10.join(dataDir, "run-results");
1803
+ this.testScriptsDir = path10.join(dataDir, "test-scripts");
1802
1804
  this.ensureDirectories();
1803
1805
  }
1804
1806
  /**
@@ -1824,7 +1826,7 @@ var RunResultStorageService = class {
1824
1826
  const results = [];
1825
1827
  for (const file of files) {
1826
1828
  try {
1827
- const content = fs5.readFileSync(path9.join(this.runResultsDir, file), "utf-8");
1829
+ const content = fs5.readFileSync(path10.join(this.runResultsDir, file), "utf-8");
1828
1830
  results.push(JSON.parse(content));
1829
1831
  } catch {
1830
1832
  logger3.warn("Failed to read run result file", { file });
@@ -1839,7 +1841,7 @@ var RunResultStorageService = class {
1839
1841
  * Get a run result by ID.
1840
1842
  */
1841
1843
  getRunResult(runId) {
1842
- const filePath = path9.join(this.runResultsDir, `${runId}.json`);
1844
+ const filePath = path10.join(this.runResultsDir, `${runId}.json`);
1843
1845
  if (!fs5.existsSync(filePath)) {
1844
1846
  return void 0;
1845
1847
  }
@@ -1854,7 +1856,7 @@ var RunResultStorageService = class {
1854
1856
  * Save a run result.
1855
1857
  */
1856
1858
  saveRunResult(result) {
1857
- const filePath = path9.join(this.runResultsDir, `${result.id}.json`);
1859
+ const filePath = path10.join(this.runResultsDir, `${result.id}.json`);
1858
1860
  fs5.writeFileSync(filePath, JSON.stringify(result, null, 2));
1859
1861
  }
1860
1862
  /**
@@ -1907,7 +1909,7 @@ var RunResultStorageService = class {
1907
1909
  const scripts = [];
1908
1910
  for (const file of files) {
1909
1911
  try {
1910
- const content = fs5.readFileSync(path9.join(this.testScriptsDir, file), "utf-8");
1912
+ const content = fs5.readFileSync(path10.join(this.testScriptsDir, file), "utf-8");
1911
1913
  scripts.push(JSON.parse(content));
1912
1914
  } catch {
1913
1915
  logger3.warn("Failed to read test script file", { file });
@@ -1922,7 +1924,7 @@ var RunResultStorageService = class {
1922
1924
  * Get a test script by ID.
1923
1925
  */
1924
1926
  getTestScript(testScriptId) {
1925
- const filePath = path9.join(this.testScriptsDir, `${testScriptId}.json`);
1927
+ const filePath = path10.join(this.testScriptsDir, `${testScriptId}.json`);
1926
1928
  if (!fs5.existsSync(filePath)) {
1927
1929
  return void 0;
1928
1930
  }
@@ -1937,7 +1939,7 @@ var RunResultStorageService = class {
1937
1939
  * Save a test script.
1938
1940
  */
1939
1941
  saveTestScript(script) {
1940
- const filePath = path9.join(this.testScriptsDir, `${script.id}.json`);
1942
+ const filePath = path10.join(this.testScriptsDir, `${script.id}.json`);
1941
1943
  fs5.writeFileSync(filePath, JSON.stringify(script, null, 2));
1942
1944
  }
1943
1945
  /**
@@ -1987,6 +1989,246 @@ function resetRunResultStorageService() {
1987
1989
  instance = null;
1988
1990
  }
1989
1991
 
1992
+ // packages/mcps/src/shared/llm-env-constants.ts
1993
+ var LLM_ENV_PREFERENCE_KEY = "llmEnv";
1994
+ var FORWARDABLE_LLM_ENV_NAMES = [
1995
+ "MUGGLE_LLM_PROVIDER",
1996
+ "MUGGLE_LLM_BASE_URL",
1997
+ "MUGGLE_LLM_API_KEY",
1998
+ "MUGGLE_LLM_MODEL",
1999
+ "MUGGLE_LLM_MODEL_PREDICTION",
2000
+ "MUGGLE_LLM_MODEL_SUMMARIZER",
2001
+ "MUGGLE_LLM_MODEL_EVAL",
2002
+ "MUGGLE_LLM_MAX_TOKENS",
2003
+ "MUGGLE_LLM_TEMPERATURE",
2004
+ "MUGGLE_LLM_TOP_P",
2005
+ "MUGGLE_LLM_TIMEOUT_MS"
2006
+ ];
2007
+
2008
+ // packages/mcps/src/shared/preferences-types.ts
2009
+ var PreferenceKey = /* @__PURE__ */ ((PreferenceKey2) => {
2010
+ PreferenceKey2["AutoLogin"] = "autoLogin";
2011
+ PreferenceKey2["AutoSelectProject"] = "autoSelectProject";
2012
+ PreferenceKey2["AutoSelectLocalHost"] = "autoSelectLocalHost";
2013
+ PreferenceKey2["ShowElectronBrowser"] = "showElectronBrowser";
2014
+ PreferenceKey2["OpenTestResultsAfterRun"] = "openTestResultsAfterRun";
2015
+ PreferenceKey2["DefaultExecutionMode"] = "defaultExecutionMode";
2016
+ PreferenceKey2["SuggestRelatedUseCases"] = "suggestRelatedUseCases";
2017
+ PreferenceKey2["SuggestRelatedTestCases"] = "suggestRelatedTestCases";
2018
+ PreferenceKey2["AutoDetectChanges"] = "autoDetectChanges";
2019
+ PreferenceKey2["PostPRVisualWalkthrough"] = "postPRVisualWalkthrough";
2020
+ PreferenceKey2["AutoCreatePR"] = "autoCreatePR";
2021
+ PreferenceKey2["CheckForUpdates"] = "checkForUpdates";
2022
+ PreferenceKey2["VerboseOutput"] = "verboseOutput";
2023
+ PreferenceKey2["AutoUseWorktree"] = "autoUseWorktree";
2024
+ PreferenceKey2["AutoRebase"] = "autoRebase";
2025
+ PreferenceKey2["AutoCleanup"] = "autoCleanup";
2026
+ PreferenceKey2["AutoE2ETest"] = "autoE2ETest";
2027
+ PreferenceKey2["AutoResolveConflicts"] = "autoResolveConflicts";
2028
+ PreferenceKey2["AutoReuseValidationContext"] = "autoReuseValidationContext";
2029
+ PreferenceKey2["AutoRouteBuildToMuggleDo"] = "autoRouteBuildToMuggleDo";
2030
+ PreferenceKey2["AutoWatchPR"] = "autoWatchPR";
2031
+ PreferenceKey2["WatcherLifetime"] = "watcherLifetime";
2032
+ PreferenceKey2["ReusePreparePlan"] = "reusePreparePlan";
2033
+ return PreferenceKey2;
2034
+ })(PreferenceKey || {});
2035
+ var PreferenceValue = /* @__PURE__ */ ((PreferenceValue2) => {
2036
+ PreferenceValue2["Always"] = "always";
2037
+ PreferenceValue2["Ask"] = "ask";
2038
+ PreferenceValue2["Never"] = "never";
2039
+ PreferenceValue2["Local"] = "local";
2040
+ PreferenceValue2["Remote"] = "remote";
2041
+ PreferenceValue2["OneDay"] = "1d";
2042
+ PreferenceValue2["SevenDays"] = "7d";
2043
+ return PreferenceValue2;
2044
+ })(PreferenceValue || {});
2045
+
2046
+ // packages/mcps/src/shared/preferences-constants.ts
2047
+ var PREFERENCES_FILE_NAME = "preferences.json";
2048
+ var PREFERENCES_PROJECT_DIR_NAME = ".muggle-ai";
2049
+ var PREFERENCES_VERSION = 1;
2050
+ var DEFAULT_PREFERENCES = {
2051
+ ["autoLogin" /* AutoLogin */]: "always" /* Always */,
2052
+ ["autoSelectProject" /* AutoSelectProject */]: "always" /* Always */,
2053
+ ["autoSelectLocalHost" /* AutoSelectLocalHost */]: "always" /* Always */,
2054
+ ["showElectronBrowser" /* ShowElectronBrowser */]: "always" /* Always */,
2055
+ ["openTestResultsAfterRun" /* OpenTestResultsAfterRun */]: "always" /* Always */,
2056
+ ["defaultExecutionMode" /* DefaultExecutionMode */]: "local" /* Local */,
2057
+ ["suggestRelatedUseCases" /* SuggestRelatedUseCases */]: "always" /* Always */,
2058
+ ["suggestRelatedTestCases" /* SuggestRelatedTestCases */]: "always" /* Always */,
2059
+ ["autoDetectChanges" /* AutoDetectChanges */]: "always" /* Always */,
2060
+ ["postPRVisualWalkthrough" /* PostPRVisualWalkthrough */]: "always" /* Always */,
2061
+ ["autoCreatePR" /* AutoCreatePR */]: "always" /* Always */,
2062
+ ["checkForUpdates" /* CheckForUpdates */]: "always" /* Always */,
2063
+ ["verboseOutput" /* VerboseOutput */]: "never" /* Never */,
2064
+ ["autoUseWorktree" /* AutoUseWorktree */]: "always" /* Always */,
2065
+ ["autoRebase" /* AutoRebase */]: "always" /* Always */,
2066
+ ["autoCleanup" /* AutoCleanup */]: "always" /* Always */,
2067
+ ["autoE2ETest" /* AutoE2ETest */]: "always" /* Always */,
2068
+ ["autoResolveConflicts" /* AutoResolveConflicts */]: "always" /* Always */,
2069
+ ["autoReuseValidationContext" /* AutoReuseValidationContext */]: "always" /* Always */,
2070
+ ["autoRouteBuildToMuggleDo" /* AutoRouteBuildToMuggleDo */]: "always" /* Always */,
2071
+ ["autoWatchPR" /* AutoWatchPR */]: "always" /* Always */,
2072
+ ["reusePreparePlan" /* ReusePreparePlan */]: "always" /* Always */,
2073
+ ["watcherLifetime" /* WatcherLifetime */]: "7d" /* SevenDays */
2074
+ };
2075
+ var ALWAYS_ASK_NEVER = [
2076
+ "always" /* Always */,
2077
+ "ask" /* Ask */,
2078
+ "never" /* Never */
2079
+ ];
2080
+ var ALWAYS_ASK = [
2081
+ "always" /* Always */,
2082
+ "ask" /* Ask */
2083
+ ];
2084
+ var LOCAL_REMOTE_ASK = [
2085
+ "local" /* Local */,
2086
+ "remote" /* Remote */,
2087
+ "ask" /* Ask */
2088
+ ];
2089
+ var WATCHER_LIFETIMES = [
2090
+ "1d" /* OneDay */,
2091
+ "7d" /* SevenDays */,
2092
+ "never" /* Never */
2093
+ ];
2094
+ var PREFERENCE_ALLOWED_VALUES = {
2095
+ ["autoLogin" /* AutoLogin */]: ALWAYS_ASK_NEVER,
2096
+ ["autoSelectProject" /* AutoSelectProject */]: ALWAYS_ASK_NEVER,
2097
+ ["autoSelectLocalHost" /* AutoSelectLocalHost */]: ALWAYS_ASK_NEVER,
2098
+ ["showElectronBrowser" /* ShowElectronBrowser */]: ALWAYS_ASK_NEVER,
2099
+ ["openTestResultsAfterRun" /* OpenTestResultsAfterRun */]: ALWAYS_ASK_NEVER,
2100
+ ["defaultExecutionMode" /* DefaultExecutionMode */]: LOCAL_REMOTE_ASK,
2101
+ ["suggestRelatedUseCases" /* SuggestRelatedUseCases */]: ALWAYS_ASK_NEVER,
2102
+ ["suggestRelatedTestCases" /* SuggestRelatedTestCases */]: ALWAYS_ASK_NEVER,
2103
+ ["autoDetectChanges" /* AutoDetectChanges */]: ALWAYS_ASK_NEVER,
2104
+ ["postPRVisualWalkthrough" /* PostPRVisualWalkthrough */]: ALWAYS_ASK_NEVER,
2105
+ ["autoCreatePR" /* AutoCreatePR */]: ALWAYS_ASK_NEVER,
2106
+ ["checkForUpdates" /* CheckForUpdates */]: ALWAYS_ASK_NEVER,
2107
+ ["verboseOutput" /* VerboseOutput */]: ALWAYS_ASK_NEVER,
2108
+ ["autoUseWorktree" /* AutoUseWorktree */]: ALWAYS_ASK_NEVER,
2109
+ ["autoRebase" /* AutoRebase */]: ALWAYS_ASK_NEVER,
2110
+ ["autoCleanup" /* AutoCleanup */]: ALWAYS_ASK_NEVER,
2111
+ ["autoE2ETest" /* AutoE2ETest */]: ALWAYS_ASK,
2112
+ ["autoResolveConflicts" /* AutoResolveConflicts */]: ALWAYS_ASK_NEVER,
2113
+ ["autoReuseValidationContext" /* AutoReuseValidationContext */]: ALWAYS_ASK_NEVER,
2114
+ ["autoRouteBuildToMuggleDo" /* AutoRouteBuildToMuggleDo */]: ALWAYS_ASK_NEVER,
2115
+ ["autoWatchPR" /* AutoWatchPR */]: ALWAYS_ASK_NEVER,
2116
+ ["reusePreparePlan" /* ReusePreparePlan */]: ALWAYS_ASK_NEVER,
2117
+ ["watcherLifetime" /* WatcherLifetime */]: WATCHER_LIFETIMES
2118
+ };
2119
+ var PREFERENCES_SCHEMA = {
2120
+ ["autoLogin" /* AutoLogin */]: {
2121
+ description: "When a tool requires auth and saved credentials exist, reuse them without prompting"
2122
+ },
2123
+ ["autoSelectProject" /* AutoSelectProject */]: {
2124
+ description: "When a skill needs a Muggle Test project and one was used previously in this repo, reuse it without prompting"
2125
+ },
2126
+ ["autoSelectLocalHost" /* AutoSelectLocalHost */]: {
2127
+ description: "When running local tests, reuse the dev server URL from the previous run in this repo without prompting"
2128
+ },
2129
+ ["showElectronBrowser" /* ShowElectronBrowser */]: {
2130
+ description: "When running local E2E tests, show the Electron browser window"
2131
+ },
2132
+ ["openTestResultsAfterRun" /* OpenTestResultsAfterRun */]: {
2133
+ description: "After a local E2E test run completes, open the per-run results page on Muggle Test dashboard"
2134
+ },
2135
+ ["defaultExecutionMode" /* DefaultExecutionMode */]: {
2136
+ description: "When a skill supports both local and remote test execution, which to default to"
2137
+ },
2138
+ ["suggestRelatedUseCases" /* SuggestRelatedUseCases */]: {
2139
+ description: "After creating or running a use case, suggest related use cases to add"
2140
+ },
2141
+ ["suggestRelatedTestCases" /* SuggestRelatedTestCases */]: {
2142
+ description: "After creating or running a test case, suggest related test cases to add"
2143
+ },
2144
+ ["autoDetectChanges" /* AutoDetectChanges */]: {
2145
+ description: "When running muggle-test, scan local git changes and map to affected test cases"
2146
+ },
2147
+ ["postPRVisualWalkthrough" /* PostPRVisualWalkthrough */]: {
2148
+ description: "After test results are available, post visual walkthrough comment with screenshots to the PR"
2149
+ },
2150
+ ["autoCreatePR" /* AutoCreatePR */]: {
2151
+ 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"
2152
+ },
2153
+ ["checkForUpdates" /* CheckForUpdates */]: {
2154
+ description: "At session start, check if a newer Muggle Test version is available and notify"
2155
+ },
2156
+ ["verboseOutput" /* VerboseOutput */]: {
2157
+ description: "Show detailed progress logs during skill and tool execution"
2158
+ },
2159
+ ["autoUseWorktree" /* AutoUseWorktree */]: {
2160
+ description: "When starting non-trivial development work, create a git worktree to isolate the change from the current checkout"
2161
+ },
2162
+ ["autoRebase" /* AutoRebase */]: {
2163
+ description: "Before running dev servers or E2E tests on a branch behind origin, rebase onto the default branch first"
2164
+ },
2165
+ ["autoCleanup" /* AutoCleanup */]: {
2166
+ description: "After a PR is merged, automatically run the cleanup sequence: remove worktree, delete branches, clear local artifacts, prune [gone] branches"
2167
+ },
2168
+ ["autoE2ETest" /* AutoE2ETest */]: {
2169
+ 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)"
2170
+ },
2171
+ ["autoResolveConflicts" /* AutoResolveConflicts */]: {
2172
+ description: "When a rebase onto the default branch hits conflicts, resolve them autonomously behind a verify-or-rollback gate instead of aborting and escalating"
2173
+ },
2174
+ ["autoReuseValidationContext" /* AutoReuseValidationContext */]: {
2175
+ description: "When a prior session left an E2E validation context for this working tree, reuse it instead of re-asking the validation questions"
2176
+ },
2177
+ ["autoRouteBuildToMuggleDo" /* AutoRouteBuildToMuggleDo */]: {
2178
+ description: "When a prompt looks like a build/implement/fix request, route it through the /muggle-do pipeline (requirements \u2192 build \u2192 impact \u2192 tests \u2192 E2E \u2192 PR \u2192 watcher)"
2179
+ },
2180
+ ["autoWatchPR" /* AutoWatchPR */]: {
2181
+ description: "After a test run opens a PR, start a muggle-pr-followup watcher that polls for new reviews and hands them to /muggle-do"
2182
+ },
2183
+ ["reusePreparePlan" /* ReusePreparePlan */]: {
2184
+ description: "Reuse the saved prepare plan for this stack (skip discovery, jump to check-running + smoke-test) instead of rediscovering from scratch"
2185
+ },
2186
+ ["watcherLifetime" /* WatcherLifetime */]: {
2187
+ description: "How long a muggle-pr-followup watch loop polls before retiring itself \u2014 1d, 7d, or never (unbounded, which leaves no time-based reaper for an orphaned loop)"
2188
+ }
2189
+ };
2190
+
2191
+ // packages/mcps/src/shared/llm-env-service.ts
2192
+ function resolveLlmEnvOverrides(dataDirOverride2) {
2193
+ const filePath = path10.join(getDataDir(), PREFERENCES_FILE_NAME);
2194
+ let block;
2195
+ try {
2196
+ if (!fs5.existsSync(filePath)) {
2197
+ return {};
2198
+ }
2199
+ const raw = JSON.parse(fs5.readFileSync(filePath, "utf-8"));
2200
+ block = raw[LLM_ENV_PREFERENCE_KEY];
2201
+ } catch {
2202
+ return {};
2203
+ }
2204
+ if (!block || typeof block !== "object" || Array.isArray(block)) {
2205
+ return {};
2206
+ }
2207
+ const overrides = {};
2208
+ for (const [name, value] of Object.entries(block)) {
2209
+ if (!FORWARDABLE_LLM_ENV_NAMES.includes(name)) {
2210
+ continue;
2211
+ }
2212
+ if (value === null || value === void 0 || value === "") {
2213
+ continue;
2214
+ }
2215
+ overrides[name] = String(value);
2216
+ }
2217
+ return overrides;
2218
+ }
2219
+ function applyLlmEnvOverrides(baseEnv, dataDirOverride2) {
2220
+ const overrides = resolveLlmEnvOverrides();
2221
+ const appliedNames = [];
2222
+ for (const [name, value] of Object.entries(overrides)) {
2223
+ if (baseEnv[name] !== void 0 && baseEnv[name] !== "") {
2224
+ continue;
2225
+ }
2226
+ baseEnv[name] = value;
2227
+ appliedNames.push(name);
2228
+ }
2229
+ return { env: baseEnv, appliedNames };
2230
+ }
2231
+
1990
2232
  // packages/mcps/src/mcp/local/services/replay-url-rewrite.ts
1991
2233
  function rewriteActionScriptUrls(params) {
1992
2234
  const { actionScript, originalUrl, localUrl } = params;
@@ -2162,13 +2404,13 @@ var MODIFY_LOCK_STALE_MS = 5e3;
2162
2404
 
2163
2405
  // packages/mcps/src/mcp/local/services/local-execution-lock.ts
2164
2406
  function lockFilePath() {
2165
- return path9.join(getDataDir(), LOCK_FILE_NAME);
2407
+ return path10.join(getDataDir(), LOCK_FILE_NAME);
2166
2408
  }
2167
2409
  function modifyLockPath() {
2168
2410
  return lockFilePath() + MODIFY_LOCK_SUFFIX;
2169
2411
  }
2170
2412
  function normalizeCwd(cwd) {
2171
- const resolved = path9.resolve(cwd);
2413
+ const resolved = path10.resolve(cwd);
2172
2414
  return os4.platform() === "win32" ? resolved.toLowerCase() : resolved;
2173
2415
  }
2174
2416
  function pidIsAlive(pid) {
@@ -2184,7 +2426,7 @@ function pidIsAlive(pid) {
2184
2426
  }
2185
2427
  async function readState() {
2186
2428
  try {
2187
- const raw = await fs8.readFile(lockFilePath(), "utf-8");
2429
+ const raw = await fs9.readFile(lockFilePath(), "utf-8");
2188
2430
  return JSON.parse(raw);
2189
2431
  } catch (error) {
2190
2432
  if (error.code === "ENOENT") return null;
@@ -2192,28 +2434,28 @@ async function readState() {
2192
2434
  }
2193
2435
  }
2194
2436
  async function writeState(state2) {
2195
- await fs8.writeFile(lockFilePath(), JSON.stringify(state2, null, 2), "utf-8");
2437
+ await fs9.writeFile(lockFilePath(), JSON.stringify(state2, null, 2), "utf-8");
2196
2438
  }
2197
2439
  async function deleteStateFile() {
2198
2440
  try {
2199
- await fs8.unlink(lockFilePath());
2441
+ await fs9.unlink(lockFilePath());
2200
2442
  } catch (error) {
2201
2443
  if (error.code !== "ENOENT") throw error;
2202
2444
  }
2203
2445
  }
2204
2446
  async function withModifyLock(fn) {
2205
- await fs8.mkdir(path9.dirname(modifyLockPath()), { recursive: true });
2447
+ await fs9.mkdir(path10.dirname(modifyLockPath()), { recursive: true });
2206
2448
  const modifyPath = modifyLockPath();
2207
2449
  let fileHandle = null;
2208
2450
  while (fileHandle === null) {
2209
2451
  try {
2210
- fileHandle = await fs8.open(modifyPath, "wx");
2452
+ fileHandle = await fs9.open(modifyPath, "wx");
2211
2453
  } catch (error) {
2212
2454
  if (error.code !== "EEXIST") throw error;
2213
2455
  try {
2214
- const stat3 = await fs8.stat(modifyPath);
2456
+ const stat3 = await fs9.stat(modifyPath);
2215
2457
  if (Date.now() - stat3.mtimeMs > MODIFY_LOCK_STALE_MS) {
2216
- await fs8.unlink(modifyPath).catch(() => void 0);
2458
+ await fs9.unlink(modifyPath).catch(() => void 0);
2217
2459
  continue;
2218
2460
  }
2219
2461
  } catch {
@@ -2226,7 +2468,7 @@ async function withModifyLock(fn) {
2226
2468
  return await fn();
2227
2469
  } finally {
2228
2470
  await fileHandle.close().catch(() => void 0);
2229
- await fs8.unlink(modifyPath).catch(() => void 0);
2471
+ await fs9.unlink(modifyPath).catch(() => void 0);
2230
2472
  }
2231
2473
  }
2232
2474
  async function tryAcquireOnce(params) {
@@ -2365,12 +2607,12 @@ function extractStudioCloudRefs(studioReturnedResult) {
2365
2607
  return refs;
2366
2608
  }
2367
2609
  async function readCloudRefsFile(inputFilePath) {
2368
- const cloudRefsPath = path9.join(
2369
- path9.dirname(inputFilePath),
2370
- `cloudrefs_${path9.basename(inputFilePath)}`
2610
+ const cloudRefsPath = path10.join(
2611
+ path10.dirname(inputFilePath),
2612
+ `cloudrefs_${path10.basename(inputFilePath)}`
2371
2613
  );
2372
2614
  try {
2373
- return JSON.parse(await fs8.readFile(cloudRefsPath, "utf-8"));
2615
+ return JSON.parse(await fs9.readFile(cloudRefsPath, "utf-8"));
2374
2616
  } catch {
2375
2617
  return void 0;
2376
2618
  }
@@ -2389,15 +2631,15 @@ function getAuthenticatedUserId() {
2389
2631
  }
2390
2632
  async function findPackageJsonAsync() {
2391
2633
  const currentFileUrl = import.meta.url;
2392
- const currentDir = path9.dirname(fileURLToPath(currentFileUrl));
2634
+ const currentDir = path10.dirname(fileURLToPath(currentFileUrl));
2393
2635
  const candidatePaths = [
2394
- path9.resolve(currentDir, "../../../package.json"),
2395
- path9.resolve(currentDir, "../package.json"),
2396
- path9.resolve(currentDir, "../../package.json")
2636
+ path10.resolve(currentDir, "../../../package.json"),
2637
+ path10.resolve(currentDir, "../package.json"),
2638
+ path10.resolve(currentDir, "../../package.json")
2397
2639
  ];
2398
2640
  for (const candidatePath of candidatePaths) {
2399
2641
  try {
2400
- const packageJsonRaw = await fs8.readFile(candidatePath, "utf-8");
2642
+ const packageJsonRaw = await fs9.readFile(candidatePath, "utf-8");
2401
2643
  const packageJson = JSON.parse(packageJsonRaw);
2402
2644
  if (packageJson.name === "@muggleai/works" && packageJson.version && packageJson.muggleConfig?.electronAppVersion) {
2403
2645
  return {
@@ -2443,20 +2685,20 @@ function buildStudioAuthContent() {
2443
2685
  }
2444
2686
  async function ensureTempDir() {
2445
2687
  const config = getConfig();
2446
- const tempDir = path9.join(config.localQa.dataDir, "temp");
2447
- await fs8.mkdir(tempDir, { recursive: true });
2688
+ const tempDir = path10.join(config.localQa.dataDir, "temp");
2689
+ await fs9.mkdir(tempDir, { recursive: true });
2448
2690
  return tempDir;
2449
2691
  }
2450
2692
  async function writeTempFile(params) {
2451
2693
  const tempDir = await ensureTempDir();
2452
- const filePath = path9.join(tempDir, params.filename);
2453
- await fs8.writeFile(filePath, JSON.stringify(params.data, null, 2));
2694
+ const filePath = path10.join(tempDir, params.filename);
2695
+ await fs9.writeFile(filePath, JSON.stringify(params.data, null, 2));
2454
2696
  return filePath;
2455
2697
  }
2456
2698
  async function cleanupTempFiles(params) {
2457
2699
  for (const filePath of params.filePaths) {
2458
2700
  try {
2459
- await fs8.unlink(filePath);
2701
+ await fs9.unlink(filePath);
2460
2702
  } catch {
2461
2703
  }
2462
2704
  }
@@ -2464,12 +2706,12 @@ async function cleanupTempFiles(params) {
2464
2706
  async function moveResultsToArtifacts(params) {
2465
2707
  const storageService = getStorageService();
2466
2708
  const sessionsDir = storageService.getSessionsDir();
2467
- const artifactsDir = path9.join(sessionsDir, params.runId);
2468
- const actionScriptPath = path9.join(artifactsDir, "action-script.json");
2469
- await fs8.mkdir(artifactsDir, { recursive: true });
2470
- await fs8.copyFile(params.generatedScriptPath, actionScriptPath);
2709
+ const artifactsDir = path10.join(sessionsDir, params.runId);
2710
+ const actionScriptPath = path10.join(artifactsDir, "action-script.json");
2711
+ await fs9.mkdir(artifactsDir, { recursive: true });
2712
+ await fs9.copyFile(params.generatedScriptPath, actionScriptPath);
2471
2713
  try {
2472
- await fs8.unlink(params.generatedScriptPath);
2714
+ await fs9.unlink(params.generatedScriptPath);
2473
2715
  } catch {
2474
2716
  }
2475
2717
  return artifactsDir;
@@ -2477,13 +2719,13 @@ async function moveResultsToArtifacts(params) {
2477
2719
  async function writeExecutionLogs(params) {
2478
2720
  const storageService = getStorageService();
2479
2721
  const sessionsDir = storageService.getSessionsDir();
2480
- const artifactsDir = path9.join(sessionsDir, params.runId);
2481
- await fs8.mkdir(artifactsDir, { recursive: true });
2482
- const stdoutPath = path9.join(artifactsDir, "stdout.log");
2483
- const stderrPath = path9.join(artifactsDir, "stderr.log");
2722
+ const artifactsDir = path10.join(sessionsDir, params.runId);
2723
+ await fs9.mkdir(artifactsDir, { recursive: true });
2724
+ const stdoutPath = path10.join(artifactsDir, "stdout.log");
2725
+ const stderrPath = path10.join(artifactsDir, "stderr.log");
2484
2726
  await Promise.all([
2485
- fs8.writeFile(stdoutPath, params.stdout, "utf-8"),
2486
- fs8.writeFile(stderrPath, params.stderr, "utf-8")
2727
+ fs9.writeFile(stdoutPath, params.stdout, "utf-8"),
2728
+ fs9.writeFile(stderrPath, params.stderr, "utf-8")
2487
2729
  ]);
2488
2730
  logger4.info("Wrote execution logs to artifacts directory", {
2489
2731
  runId: params.runId,
@@ -2493,10 +2735,10 @@ async function writeExecutionLogs(params) {
2493
2735
  async function preserveElectronRuntimeArtifacts(params) {
2494
2736
  try {
2495
2737
  const tempDir = await ensureTempDir();
2496
- const markerPath = path9.join(tempDir, `${params.runId}_runtime_dir.txt`);
2738
+ const markerPath = path10.join(tempDir, `${params.runId}_runtime_dir.txt`);
2497
2739
  let runtimeDir = null;
2498
2740
  try {
2499
- const marker = await fs8.readFile(markerPath, "utf-8");
2741
+ const marker = await fs9.readFile(markerPath, "utf-8");
2500
2742
  const trimmed = marker.trim();
2501
2743
  if (trimmed.length > 0) runtimeDir = trimmed;
2502
2744
  } catch (markerReadError) {
@@ -2518,25 +2760,25 @@ async function preserveElectronRuntimeArtifacts(params) {
2518
2760
  return;
2519
2761
  }
2520
2762
  try {
2521
- await fs8.access(runtimeDir);
2763
+ await fs9.access(runtimeDir);
2522
2764
  } catch {
2523
2765
  logger4.warn("Electron runtime dir referenced but missing on disk", {
2524
2766
  runId: params.runId,
2525
2767
  runtimeDir
2526
2768
  });
2527
- await fs8.unlink(markerPath).catch(() => void 0);
2769
+ await fs9.unlink(markerPath).catch(() => void 0);
2528
2770
  return;
2529
2771
  }
2530
2772
  const storage = getStorageService();
2531
- const destDir = path9.join(storage.getSessionsDir(), params.runId, "electron-runtime");
2532
- await fs8.mkdir(destDir, { recursive: true });
2533
- await fs8.cp(runtimeDir, destDir, { recursive: true });
2773
+ const destDir = path10.join(storage.getSessionsDir(), params.runId, "electron-runtime");
2774
+ await fs9.mkdir(destDir, { recursive: true });
2775
+ await fs9.cp(runtimeDir, destDir, { recursive: true });
2534
2776
  logger4.info("Preserved Electron runtime artifacts", {
2535
2777
  runId: params.runId,
2536
2778
  sourceDir: runtimeDir,
2537
2779
  destDir
2538
2780
  });
2539
- await fs8.unlink(markerPath).catch(() => void 0);
2781
+ await fs9.unlink(markerPath).catch(() => void 0);
2540
2782
  } catch (preserveError) {
2541
2783
  logger4.warn("Failed to preserve Electron runtime artifacts", {
2542
2784
  runId: params.runId,
@@ -2545,14 +2787,14 @@ async function preserveElectronRuntimeArtifacts(params) {
2545
2787
  }
2546
2788
  }
2547
2789
  async function scanForRuntimeDirByMtime(params) {
2548
- const runtimeRoot = path9.join(params.electronCwd, "user_data", "runtime");
2790
+ const runtimeRoot = path10.join(params.electronCwd, "user_data", "runtime");
2549
2791
  try {
2550
- const entries = await fs8.readdir(runtimeRoot, { withFileTypes: true });
2792
+ const entries = await fs9.readdir(runtimeRoot, { withFileTypes: true });
2551
2793
  let bestCandidate = null;
2552
2794
  for (const entry of entries) {
2553
2795
  if (!entry.isDirectory()) continue;
2554
- const fullPath = path9.join(runtimeRoot, entry.name);
2555
- const stat3 = await fs8.stat(fullPath);
2796
+ const fullPath = path10.join(runtimeRoot, entry.name);
2797
+ const stat3 = await fs9.stat(fullPath);
2556
2798
  if (stat3.mtimeMs < params.spawnAtMs) continue;
2557
2799
  if (bestCandidate === null || stat3.mtimeMs > bestCandidate.mtimeMs) {
2558
2800
  bestCandidate = { path: fullPath, mtimeMs: stat3.mtimeMs };
@@ -2597,15 +2839,18 @@ async function executeElectronAppAsync(params) {
2597
2839
  if (params.freshSession) {
2598
2840
  spawnArgs.push("--fresh-session");
2599
2841
  }
2842
+ const { env: electronEnv, appliedNames: llmEnvOverrideNames } = applyLlmEnvOverrides({
2843
+ ...process.env
2844
+ });
2845
+ delete electronEnv.ELECTRON_RUN_AS_NODE;
2600
2846
  logger4.info("Spawning electron-app for local execution", {
2601
2847
  runId: params.runId,
2602
2848
  mode,
2603
2849
  electronAppPath,
2604
- spawnArgs
2850
+ spawnArgs,
2851
+ llmEnvOverrideNames
2605
2852
  });
2606
- const electronEnv = { ...process.env };
2607
- delete electronEnv.ELECTRON_RUN_AS_NODE;
2608
- const electronCwd = path9.dirname(electronAppPath);
2853
+ const electronCwd = path10.dirname(electronAppPath);
2609
2854
  const child = spawn(electronAppPath, spawnArgs, {
2610
2855
  stdio: ["ignore", "pipe", "pipe"],
2611
2856
  env: electronEnv,
@@ -2620,7 +2865,7 @@ async function executeElectronAppAsync(params) {
2620
2865
  capturedStderr: ""
2621
2866
  };
2622
2867
  activeProcesses.set(params.runId, processInfo);
2623
- return await new Promise((resolve4, reject) => {
2868
+ return await new Promise((resolve5, reject) => {
2624
2869
  let settled = false;
2625
2870
  const finalize = (result) => {
2626
2871
  if (settled) {
@@ -2632,7 +2877,7 @@ async function executeElectronAppAsync(params) {
2632
2877
  }
2633
2878
  activeProcesses.delete(params.runId);
2634
2879
  if (result.ok) {
2635
- resolve4(result.payload);
2880
+ resolve5(result.payload);
2636
2881
  } else {
2637
2882
  reject(result.payload);
2638
2883
  }
@@ -2787,17 +3032,17 @@ ${executionResult.stdout}
2787
3032
  STDERR:
2788
3033
  ${executionResult.stderr}`;
2789
3034
  const sessionsDir = getStorageService().getSessionsDir();
2790
- const artifactsDir2 = path9.join(sessionsDir, runId);
2791
- const generatedScriptPath2 = path9.join(
2792
- path9.dirname(inputFilePath),
2793
- `gen_${path9.basename(inputFilePath)}`
3035
+ const artifactsDir2 = path10.join(sessionsDir, runId);
3036
+ const generatedScriptPath2 = path10.join(
3037
+ path10.dirname(inputFilePath),
3038
+ `gen_${path10.basename(inputFilePath)}`
2794
3039
  );
2795
3040
  let failedStudioResult;
2796
3041
  try {
2797
- const failedScriptRaw = await fs8.readFile(generatedScriptPath2, "utf-8");
3042
+ const failedScriptRaw = await fs9.readFile(generatedScriptPath2, "utf-8");
2798
3043
  failedStudioResult = JSON.parse(failedScriptRaw);
2799
- await fs8.copyFile(generatedScriptPath2, path9.join(artifactsDir2, "action-script.json"));
2800
- await fs8.unlink(generatedScriptPath2).catch(() => {
3044
+ await fs9.copyFile(generatedScriptPath2, path10.join(artifactsDir2, "action-script.json"));
3045
+ await fs9.unlink(generatedScriptPath2).catch(() => {
2801
3046
  });
2802
3047
  } catch {
2803
3048
  }
@@ -2826,11 +3071,11 @@ ${executionResult.stderr}`;
2826
3071
  errorMessage: failureMessage
2827
3072
  };
2828
3073
  }
2829
- const generatedScriptPath = path9.join(
2830
- path9.dirname(inputFilePath),
2831
- `gen_${path9.basename(inputFilePath)}`
3074
+ const generatedScriptPath = path10.join(
3075
+ path10.dirname(inputFilePath),
3076
+ `gen_${path10.basename(inputFilePath)}`
2832
3077
  );
2833
- const generatedScriptRaw = await fs8.readFile(generatedScriptPath, "utf-8");
3078
+ const generatedScriptRaw = await fs9.readFile(generatedScriptPath, "utf-8");
2834
3079
  const generatedScript = JSON.parse(generatedScriptRaw);
2835
3080
  const generatedSteps = generatedScript.steps;
2836
3081
  if (!Array.isArray(generatedSteps)) {
@@ -2973,7 +3218,7 @@ STDOUT:
2973
3218
  ${executionResult.stdout}
2974
3219
  STDERR:
2975
3220
  ${executionResult.stderr}`;
2976
- const artifactsDir2 = path9.join(getStorageService().getSessionsDir(), runId);
3221
+ const artifactsDir2 = path10.join(getStorageService().getSessionsDir(), runId);
2977
3222
  storage.updateRunResult(runId, {
2978
3223
  status: "failed",
2979
3224
  executionTimeMs,
@@ -2991,7 +3236,7 @@ ${executionResult.stderr}`;
2991
3236
  errorMessage: failureMessage
2992
3237
  };
2993
3238
  }
2994
- const artifactsDir = path9.join(getStorageService().getSessionsDir(), runId);
3239
+ const artifactsDir = path10.join(getStorageService().getSessionsDir(), runId);
2995
3240
  const replayStudioResult = await readCloudRefsFile(inputFilePath);
2996
3241
  const replayStudioCloudRefs = extractStudioCloudRefs(replayStudioResult);
2997
3242
  storage.updateRunResult(runId, {
@@ -3428,7 +3673,7 @@ var PREFS_FILENAME = "preferences.json";
3428
3673
  var OAUTH_SESSION_FILENAME = "oauth-session.json";
3429
3674
  var RELEASE_MANIFEST_FILENAME = "release-manifest.json";
3430
3675
  function getDataDir3() {
3431
- return path9.join(os4.homedir(), DATA_DIR_NAME2);
3676
+ return path10.join(os4.homedir(), DATA_DIR_NAME2);
3432
3677
  }
3433
3678
 
3434
3679
  var cachedInstallId;
@@ -6257,186 +6502,17 @@ var CleanupSessionsInputSchema = z.object({
6257
6502
  max_age_days: z.number().int().min(0).optional().describe("Maximum age of sessions to keep (in days). Sessions older than this will be deleted. Defaults to 30.")
6258
6503
  });
6259
6504
  var EmptyInputSchema2 = z.object({});
6260
-
6261
- // packages/mcps/src/shared/preferences-types.ts
6262
- var PreferenceKey = /* @__PURE__ */ ((PreferenceKey2) => {
6263
- PreferenceKey2["AutoLogin"] = "autoLogin";
6264
- PreferenceKey2["AutoSelectProject"] = "autoSelectProject";
6265
- PreferenceKey2["AutoSelectLocalHost"] = "autoSelectLocalHost";
6266
- PreferenceKey2["ShowElectronBrowser"] = "showElectronBrowser";
6267
- PreferenceKey2["OpenTestResultsAfterRun"] = "openTestResultsAfterRun";
6268
- PreferenceKey2["DefaultExecutionMode"] = "defaultExecutionMode";
6269
- PreferenceKey2["SuggestRelatedUseCases"] = "suggestRelatedUseCases";
6270
- PreferenceKey2["SuggestRelatedTestCases"] = "suggestRelatedTestCases";
6271
- PreferenceKey2["AutoDetectChanges"] = "autoDetectChanges";
6272
- PreferenceKey2["PostPRVisualWalkthrough"] = "postPRVisualWalkthrough";
6273
- PreferenceKey2["AutoCreatePR"] = "autoCreatePR";
6274
- PreferenceKey2["CheckForUpdates"] = "checkForUpdates";
6275
- PreferenceKey2["VerboseOutput"] = "verboseOutput";
6276
- PreferenceKey2["AutoUseWorktree"] = "autoUseWorktree";
6277
- PreferenceKey2["AutoRebase"] = "autoRebase";
6278
- PreferenceKey2["AutoCleanup"] = "autoCleanup";
6279
- PreferenceKey2["AutoE2ETest"] = "autoE2ETest";
6280
- PreferenceKey2["AutoResolveConflicts"] = "autoResolveConflicts";
6281
- PreferenceKey2["AutoReuseValidationContext"] = "autoReuseValidationContext";
6282
- PreferenceKey2["AutoRouteBuildToMuggleDo"] = "autoRouteBuildToMuggleDo";
6283
- PreferenceKey2["AutoWatchPR"] = "autoWatchPR";
6284
- PreferenceKey2["ReusePreparePlan"] = "reusePreparePlan";
6285
- return PreferenceKey2;
6286
- })(PreferenceKey || {});
6287
- var PreferenceValue = /* @__PURE__ */ ((PreferenceValue2) => {
6288
- PreferenceValue2["Always"] = "always";
6289
- PreferenceValue2["Ask"] = "ask";
6290
- PreferenceValue2["Never"] = "never";
6291
- PreferenceValue2["Local"] = "local";
6292
- PreferenceValue2["Remote"] = "remote";
6293
- return PreferenceValue2;
6294
- })(PreferenceValue || {});
6295
-
6296
- // packages/mcps/src/shared/preferences-constants.ts
6297
- var PREFERENCES_FILE_NAME = "preferences.json";
6298
- var PREFERENCES_PROJECT_DIR_NAME = ".muggle-ai";
6299
- var PREFERENCES_VERSION = 1;
6300
- var DEFAULT_PREFERENCES = {
6301
- ["autoLogin" /* AutoLogin */]: "always" /* Always */,
6302
- ["autoSelectProject" /* AutoSelectProject */]: "always" /* Always */,
6303
- ["autoSelectLocalHost" /* AutoSelectLocalHost */]: "always" /* Always */,
6304
- ["showElectronBrowser" /* ShowElectronBrowser */]: "always" /* Always */,
6305
- ["openTestResultsAfterRun" /* OpenTestResultsAfterRun */]: "always" /* Always */,
6306
- ["defaultExecutionMode" /* DefaultExecutionMode */]: "local" /* Local */,
6307
- ["suggestRelatedUseCases" /* SuggestRelatedUseCases */]: "always" /* Always */,
6308
- ["suggestRelatedTestCases" /* SuggestRelatedTestCases */]: "always" /* Always */,
6309
- ["autoDetectChanges" /* AutoDetectChanges */]: "always" /* Always */,
6310
- ["postPRVisualWalkthrough" /* PostPRVisualWalkthrough */]: "always" /* Always */,
6311
- ["autoCreatePR" /* AutoCreatePR */]: "always" /* Always */,
6312
- ["checkForUpdates" /* CheckForUpdates */]: "always" /* Always */,
6313
- ["verboseOutput" /* VerboseOutput */]: "never" /* Never */,
6314
- ["autoUseWorktree" /* AutoUseWorktree */]: "always" /* Always */,
6315
- ["autoRebase" /* AutoRebase */]: "always" /* Always */,
6316
- ["autoCleanup" /* AutoCleanup */]: "always" /* Always */,
6317
- ["autoE2ETest" /* AutoE2ETest */]: "always" /* Always */,
6318
- ["autoResolveConflicts" /* AutoResolveConflicts */]: "always" /* Always */,
6319
- ["autoReuseValidationContext" /* AutoReuseValidationContext */]: "always" /* Always */,
6320
- ["autoRouteBuildToMuggleDo" /* AutoRouteBuildToMuggleDo */]: "always" /* Always */,
6321
- ["autoWatchPR" /* AutoWatchPR */]: "always" /* Always */,
6322
- ["reusePreparePlan" /* ReusePreparePlan */]: "always" /* Always */
6323
- };
6324
- var ALWAYS_ASK_NEVER = [
6325
- "always" /* Always */,
6326
- "ask" /* Ask */,
6327
- "never" /* Never */
6328
- ];
6329
- var ALWAYS_ASK = [
6330
- "always" /* Always */,
6331
- "ask" /* Ask */
6332
- ];
6333
- var LOCAL_REMOTE_ASK = [
6334
- "local" /* Local */,
6335
- "remote" /* Remote */,
6336
- "ask" /* Ask */
6337
- ];
6338
- var PREFERENCE_ALLOWED_VALUES = {
6339
- ["autoLogin" /* AutoLogin */]: ALWAYS_ASK_NEVER,
6340
- ["autoSelectProject" /* AutoSelectProject */]: ALWAYS_ASK_NEVER,
6341
- ["autoSelectLocalHost" /* AutoSelectLocalHost */]: ALWAYS_ASK_NEVER,
6342
- ["showElectronBrowser" /* ShowElectronBrowser */]: ALWAYS_ASK_NEVER,
6343
- ["openTestResultsAfterRun" /* OpenTestResultsAfterRun */]: ALWAYS_ASK_NEVER,
6344
- ["defaultExecutionMode" /* DefaultExecutionMode */]: LOCAL_REMOTE_ASK,
6345
- ["suggestRelatedUseCases" /* SuggestRelatedUseCases */]: ALWAYS_ASK_NEVER,
6346
- ["suggestRelatedTestCases" /* SuggestRelatedTestCases */]: ALWAYS_ASK_NEVER,
6347
- ["autoDetectChanges" /* AutoDetectChanges */]: ALWAYS_ASK_NEVER,
6348
- ["postPRVisualWalkthrough" /* PostPRVisualWalkthrough */]: ALWAYS_ASK_NEVER,
6349
- ["autoCreatePR" /* AutoCreatePR */]: ALWAYS_ASK_NEVER,
6350
- ["checkForUpdates" /* CheckForUpdates */]: ALWAYS_ASK_NEVER,
6351
- ["verboseOutput" /* VerboseOutput */]: ALWAYS_ASK_NEVER,
6352
- ["autoUseWorktree" /* AutoUseWorktree */]: ALWAYS_ASK_NEVER,
6353
- ["autoRebase" /* AutoRebase */]: ALWAYS_ASK_NEVER,
6354
- ["autoCleanup" /* AutoCleanup */]: ALWAYS_ASK_NEVER,
6355
- ["autoE2ETest" /* AutoE2ETest */]: ALWAYS_ASK,
6356
- ["autoResolveConflicts" /* AutoResolveConflicts */]: ALWAYS_ASK_NEVER,
6357
- ["autoReuseValidationContext" /* AutoReuseValidationContext */]: ALWAYS_ASK_NEVER,
6358
- ["autoRouteBuildToMuggleDo" /* AutoRouteBuildToMuggleDo */]: ALWAYS_ASK_NEVER,
6359
- ["autoWatchPR" /* AutoWatchPR */]: ALWAYS_ASK_NEVER,
6360
- ["reusePreparePlan" /* ReusePreparePlan */]: ALWAYS_ASK_NEVER
6361
- };
6362
- var PREFERENCES_SCHEMA = {
6363
- ["autoLogin" /* AutoLogin */]: {
6364
- description: "When a tool requires auth and saved credentials exist, reuse them without prompting"
6365
- },
6366
- ["autoSelectProject" /* AutoSelectProject */]: {
6367
- description: "When a skill needs a Muggle Test project and one was used previously in this repo, reuse it without prompting"
6368
- },
6369
- ["autoSelectLocalHost" /* AutoSelectLocalHost */]: {
6370
- description: "When running local tests, reuse the dev server URL from the previous run in this repo without prompting"
6371
- },
6372
- ["showElectronBrowser" /* ShowElectronBrowser */]: {
6373
- description: "When running local E2E tests, show the Electron browser window"
6374
- },
6375
- ["openTestResultsAfterRun" /* OpenTestResultsAfterRun */]: {
6376
- description: "After a local E2E test run completes, open the per-run results page on Muggle Test dashboard"
6377
- },
6378
- ["defaultExecutionMode" /* DefaultExecutionMode */]: {
6379
- description: "When a skill supports both local and remote test execution, which to default to"
6380
- },
6381
- ["suggestRelatedUseCases" /* SuggestRelatedUseCases */]: {
6382
- description: "After creating or running a use case, suggest related use cases to add"
6383
- },
6384
- ["suggestRelatedTestCases" /* SuggestRelatedTestCases */]: {
6385
- description: "After creating or running a test case, suggest related test cases to add"
6386
- },
6387
- ["autoDetectChanges" /* AutoDetectChanges */]: {
6388
- description: "When running muggle-test, scan local git changes and map to affected test cases"
6389
- },
6390
- ["postPRVisualWalkthrough" /* PostPRVisualWalkthrough */]: {
6391
- description: "After test results are available, post visual walkthrough comment with screenshots to the PR"
6392
- },
6393
- ["autoCreatePR" /* AutoCreatePR */]: {
6394
- 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"
6395
- },
6396
- ["checkForUpdates" /* CheckForUpdates */]: {
6397
- description: "At session start, check if a newer Muggle Test version is available and notify"
6398
- },
6399
- ["verboseOutput" /* VerboseOutput */]: {
6400
- description: "Show detailed progress logs during skill and tool execution"
6401
- },
6402
- ["autoUseWorktree" /* AutoUseWorktree */]: {
6403
- description: "When starting non-trivial development work, create a git worktree to isolate the change from the current checkout"
6404
- },
6405
- ["autoRebase" /* AutoRebase */]: {
6406
- description: "Before running dev servers or E2E tests on a branch behind origin, rebase onto the default branch first"
6407
- },
6408
- ["autoCleanup" /* AutoCleanup */]: {
6409
- description: "After a PR is merged, automatically run the cleanup sequence: remove worktree, delete branches, clear local artifacts, prune [gone] branches"
6410
- },
6411
- ["autoE2ETest" /* AutoE2ETest */]: {
6412
- 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)"
6413
- },
6414
- ["autoResolveConflicts" /* AutoResolveConflicts */]: {
6415
- description: "When a rebase onto the default branch hits conflicts, resolve them autonomously behind a verify-or-rollback gate instead of aborting and escalating"
6416
- },
6417
- ["autoReuseValidationContext" /* AutoReuseValidationContext */]: {
6418
- description: "When a prior session left an E2E validation context for this working tree, reuse it instead of re-asking the validation questions"
6419
- },
6420
- ["autoRouteBuildToMuggleDo" /* AutoRouteBuildToMuggleDo */]: {
6421
- description: "When a prompt looks like a build/implement/fix request, route it through the /muggle-do pipeline (requirements \u2192 build \u2192 impact \u2192 tests \u2192 E2E \u2192 PR \u2192 watcher)"
6422
- },
6423
- ["autoWatchPR" /* AutoWatchPR */]: {
6424
- description: "After a test run opens a PR, start a muggle-pr-followup watcher that polls for new reviews and hands them to /muggle-do"
6425
- },
6426
- ["reusePreparePlan" /* ReusePreparePlan */]: {
6427
- description: "Reuse the saved prepare plan for this stack (skip discovery, jump to check-running + smoke-test) instead of rediscovering from scratch"
6428
- }
6429
- };
6430
-
6431
- // packages/mcps/src/mcp/local/contracts/preferences-schemas.ts
6432
6505
  var preferenceKeyValues = Object.values(PreferenceKey);
6433
6506
  var preferenceValueValues = Object.values(PreferenceValue);
6434
- var PreferencesSetInputSchema = z.object({
6435
- key: z.enum(preferenceKeyValues).describe("The preference key to set."),
6436
- value: z.enum(preferenceValueValues).describe("The value. Most keys accept always/ask/never; defaultExecutionMode accepts local/remote/ask."),
6437
- scope: z.enum(["global", "project"]).default("global").describe("Write to global (~/.muggle-ai/) or project (.muggle-ai/) preferences."),
6438
- cwd: z.string().optional().describe("Project root directory. Required when scope is 'project'.")
6439
- }).superRefine((data, ctx) => {
6507
+ var PreferencesSetInputSchema = z.object(
6508
+ {
6509
+ key: z.enum(preferenceKeyValues).describe("The preference key to set."),
6510
+ value: z.enum(preferenceValueValues).describe("The value. Most keys accept always/ask/never; defaultExecutionMode accepts local/remote/ask.")
6511
+ },
6512
+ {
6513
+ error: "Expected only { key, value }: per-project preferences were removed, so scope and cwd are no longer accepted."
6514
+ }
6515
+ ).strict().superRefine((data, ctx) => {
6440
6516
  const allowed = PREFERENCE_ALLOWED_VALUES[data.key];
6441
6517
  if (!allowed.includes(data.value)) {
6442
6518
  ctx.addIssue({
@@ -6506,9 +6582,9 @@ var TELEMETRY_DIR_NAME = "telemetry";
6506
6582
  var FAILURE_EVENTS_FILE_NAME = "failure-events.jsonl";
6507
6583
  function appendFailureEvent(record) {
6508
6584
  try {
6509
- const dir = path9.join(getDataDir(), TELEMETRY_DIR_NAME);
6585
+ const dir = path10.join(getDataDir(), TELEMETRY_DIR_NAME);
6510
6586
  fs5.mkdirSync(dir, { recursive: true });
6511
- const filePath = path9.join(dir, FAILURE_EVENTS_FILE_NAME);
6587
+ const filePath = path10.join(dir, FAILURE_EVENTS_FILE_NAME);
6512
6588
  const full = { ts: (/* @__PURE__ */ new Date()).toISOString(), ...record };
6513
6589
  fs5.appendFileSync(filePath, `${JSON.stringify(full)}
6514
6590
  `, "utf-8");
@@ -6518,9 +6594,21 @@ function appendFailureEvent(record) {
6518
6594
  });
6519
6595
  }
6520
6596
  }
6597
+
6598
+ // packages/mcps/src/shared/project-preferences-reconcile-types.ts
6599
+ var ProjectPreferencesReconcileOutcome = /* @__PURE__ */ ((ProjectPreferencesReconcileOutcome2) => {
6600
+ ProjectPreferencesReconcileOutcome2["NoAction"] = "noAction";
6601
+ ProjectPreferencesReconcileOutcome2["CopiedToGlobal"] = "copiedToGlobal";
6602
+ ProjectPreferencesReconcileOutcome2["KeysShadowed"] = "keysShadowed";
6603
+ return ProjectPreferencesReconcileOutcome2;
6604
+ })(ProjectPreferencesReconcileOutcome || {});
6605
+
6606
+ // packages/mcps/src/shared/preferences-service.ts
6607
+ function getGlobalPreferencesFilePath(dataDirOverride2) {
6608
+ return path10.join(dataDirOverride2 ?? getDataDir(), PREFERENCES_FILE_NAME);
6609
+ }
6521
6610
  function isFirstRun(dataDirOverride2) {
6522
- const filePath = path9.join(dataDirOverride2 ?? getDataDir(), PREFERENCES_FILE_NAME);
6523
- return !fs5.existsSync(filePath);
6611
+ return !fs5.existsSync(getGlobalPreferencesFilePath(dataDirOverride2));
6524
6612
  }
6525
6613
  function readPreferencesFile(filePath) {
6526
6614
  try {
@@ -6537,53 +6625,75 @@ function readPreferencesFile(filePath) {
6537
6625
  return {};
6538
6626
  }
6539
6627
  }
6540
- function readGlobalPreferences(dataDirOverride2) {
6541
- const filePath = path9.join(dataDirOverride2 ?? getDataDir(), PREFERENCES_FILE_NAME);
6542
- const partial = readPreferencesFile(filePath);
6543
- return { ...DEFAULT_PREFERENCES, ...partial };
6544
- }
6545
- function readProjectPreferences(cwd) {
6546
- const filePath = path9.join(cwd, PREFERENCES_PROJECT_DIR_NAME, PREFERENCES_FILE_NAME);
6547
- return readPreferencesFile(filePath);
6548
- }
6549
- function resolvePreferences(dataDirOverride2, cwd) {
6550
- const global = readGlobalPreferences(dataDirOverride2);
6551
- if (!cwd) {
6552
- return global;
6553
- }
6554
- const project = readProjectPreferences(cwd);
6555
- return { ...global, ...project };
6556
- }
6557
- function writePreferences2(prefs, scope, dataDirOverride2, cwd) {
6558
- const dir = scope === "project" && cwd ? path9.join(cwd, PREFERENCES_PROJECT_DIR_NAME) : dataDirOverride2 ?? getDataDir();
6559
- fs5.mkdirSync(dir, { recursive: true });
6560
- const filePath = path9.join(dir, PREFERENCES_FILE_NAME);
6561
- const existing = readPreferencesFile(filePath);
6562
- const merged = { ...existing, ...prefs };
6628
+ function writePreferencesFile(filePath, prefs) {
6563
6629
  const file = {
6630
+ ...readRawPreferencesFile(filePath),
6564
6631
  version: PREFERENCES_VERSION,
6565
- preferences: merged
6632
+ preferences: prefs
6566
6633
  };
6567
6634
  fs5.writeFileSync(filePath, `${JSON.stringify(file, null, 2)}
6568
6635
  `, "utf-8");
6569
6636
  }
6570
- function resetPreference(key, scope, dataDirOverride2, cwd) {
6571
- const dir = scope === "project" && cwd ? path9.join(cwd, PREFERENCES_PROJECT_DIR_NAME) : dataDirOverride2 ?? getDataDir();
6572
- const filePath = path9.join(dir, PREFERENCES_FILE_NAME);
6637
+ function readRawPreferencesFile(filePath) {
6638
+ try {
6639
+ if (!fs5.existsSync(filePath)) {
6640
+ return {};
6641
+ }
6642
+ return JSON.parse(fs5.readFileSync(filePath, "utf-8"));
6643
+ } catch {
6644
+ return {};
6645
+ }
6646
+ }
6647
+ function resolvePreferences(dataDirOverride2) {
6648
+ const saved = readPreferencesFile(getGlobalPreferencesFilePath(dataDirOverride2));
6649
+ return { ...DEFAULT_PREFERENCES, ...saved };
6650
+ }
6651
+ function writePreferences2(prefs, dataDirOverride2) {
6652
+ const dir = dataDirOverride2 ?? getDataDir();
6653
+ fs5.mkdirSync(dir, { recursive: true });
6654
+ const filePath = path10.join(dir, PREFERENCES_FILE_NAME);
6655
+ const existing = readPreferencesFile(filePath);
6656
+ writePreferencesFile(filePath, { ...existing, ...prefs });
6657
+ }
6658
+ function resetPreference(key, dataDirOverride2) {
6659
+ const filePath = getGlobalPreferencesFilePath(dataDirOverride2);
6573
6660
  if (!fs5.existsSync(filePath)) {
6574
6661
  return;
6575
6662
  }
6576
6663
  if (!key) {
6577
- const file2 = { version: PREFERENCES_VERSION, preferences: {} };
6578
- fs5.writeFileSync(filePath, `${JSON.stringify(file2, null, 2)}
6579
- `, "utf-8");
6664
+ writePreferencesFile(filePath, {});
6580
6665
  return;
6581
6666
  }
6582
6667
  const existing = readPreferencesFile(filePath);
6583
6668
  delete existing[key];
6584
- const file = { version: PREFERENCES_VERSION, preferences: existing };
6585
- fs5.writeFileSync(filePath, `${JSON.stringify(file, null, 2)}
6586
- `, "utf-8");
6669
+ writePreferencesFile(filePath, existing);
6670
+ }
6671
+ function reconcileProjectPreferences(cwd, dataDirOverride2) {
6672
+ const projectFilePath = path10.join(cwd, PREFERENCES_PROJECT_DIR_NAME, PREFERENCES_FILE_NAME);
6673
+ const projectPreferences = readPreferencesFile(projectFilePath);
6674
+ const projectKeys = Object.keys(projectPreferences);
6675
+ if (projectKeys.length === 0) {
6676
+ return {
6677
+ outcome: "noAction" /* NoAction */,
6678
+ projectFilePath,
6679
+ shadowedKeys: []
6680
+ };
6681
+ }
6682
+ if (isFirstRun(dataDirOverride2)) {
6683
+ writePreferences2(projectPreferences, dataDirOverride2);
6684
+ return {
6685
+ outcome: "copiedToGlobal" /* CopiedToGlobal */,
6686
+ projectFilePath,
6687
+ shadowedKeys: []
6688
+ };
6689
+ }
6690
+ const resolved = resolvePreferences(dataDirOverride2);
6691
+ const shadowedKeys = projectKeys.filter((key) => projectPreferences[key] !== resolved[key]);
6692
+ return {
6693
+ outcome: shadowedKeys.length > 0 ? "keysShadowed" /* KeysShadowed */ : "noAction" /* NoAction */,
6694
+ projectFilePath,
6695
+ shadowedKeys
6696
+ };
6587
6697
  }
6588
6698
  function validatePreference(key, value) {
6589
6699
  const validKeys = Object.values(PreferenceKey);
@@ -6596,77 +6706,134 @@ function validatePreference(key, value) {
6596
6706
  function formatPreferencesOneLiner(prefs) {
6597
6707
  return Object.entries(prefs).map(([key, value]) => `${key}=${value}`).join(" ");
6598
6708
  }
6599
- var LAST_PROJECT_FILE_NAME = "last-project.json";
6600
- var LAST_PROJECT_DIR_NAME = ".muggle-ai";
6601
- var LAST_PROJECT_VERSION = 1;
6602
- function readLastProject(cwd) {
6603
- const filePath = path9.join(cwd, LAST_PROJECT_DIR_NAME, LAST_PROJECT_FILE_NAME);
6709
+
6710
+ // packages/mcps/src/shared/cwd-keyed-cache-constants.ts
6711
+ var CWD_KEYED_CACHE_VERSION = 1;
6712
+ var LEGACY_CACHE_DIR_NAME = ".muggle-ai";
6713
+ var MUGGLE_HOME_DISPLAY_DIR = "~/.muggle-ai";
6714
+
6715
+ // packages/mcps/src/shared/cwd-keyed-cache.ts
6716
+ function resolveHomeFilePath(cache) {
6717
+ return path10.join(getDataDir(), cache.fileName);
6718
+ }
6719
+ function resolveLegacyFilePath(cache, cwd) {
6720
+ return path10.join(cwd, LEGACY_CACHE_DIR_NAME, cache.fileName);
6721
+ }
6722
+ function parseJsonFile(filePath) {
6604
6723
  try {
6605
6724
  if (!fs5.existsSync(filePath)) {
6606
6725
  return null;
6607
6726
  }
6608
- const raw = JSON.parse(fs5.readFileSync(filePath, "utf-8"));
6609
- return raw.lastProject ?? null;
6727
+ return JSON.parse(fs5.readFileSync(filePath, "utf-8"));
6610
6728
  } catch (error) {
6611
- getLogger().warn("Failed to read last-project file", {
6729
+ getLogger().warn("Failed to read cache file", {
6612
6730
  path: filePath,
6613
6731
  error: error instanceof Error ? error.message : String(error)
6614
6732
  });
6615
6733
  return null;
6616
6734
  }
6617
6735
  }
6618
- function writeLastProject(cwd, lastProject) {
6619
- const dir = path9.join(cwd, LAST_PROJECT_DIR_NAME);
6620
- fs5.mkdirSync(dir, { recursive: true });
6621
- const filePath = path9.join(dir, LAST_PROJECT_FILE_NAME);
6736
+ function readHomeEntries(cache) {
6737
+ const parsed = parseJsonFile(resolveHomeFilePath(cache));
6738
+ return parsed?.entries ?? {};
6739
+ }
6740
+ function writeHomeEntries(cache, entries) {
6741
+ fs5.mkdirSync(getDataDir(), { recursive: true });
6622
6742
  const file = {
6623
- version: LAST_PROJECT_VERSION,
6624
- lastProject: { ...lastProject, savedAt: (/* @__PURE__ */ new Date()).toISOString() }
6743
+ version: CWD_KEYED_CACHE_VERSION,
6744
+ entries
6625
6745
  };
6626
- fs5.writeFileSync(filePath, `${JSON.stringify(file, null, 2)}
6627
- `, "utf-8");
6746
+ fs5.writeFileSync(
6747
+ resolveHomeFilePath(cache),
6748
+ `${JSON.stringify(file, null, 2)}
6749
+ `,
6750
+ "utf-8"
6751
+ );
6628
6752
  }
6629
- function clearLastProject(cwd) {
6630
- const filePath = path9.join(cwd, LAST_PROJECT_DIR_NAME, LAST_PROJECT_FILE_NAME);
6631
- if (fs5.existsSync(filePath)) {
6632
- fs5.unlinkSync(filePath);
6633
- }
6753
+ function readLegacyEntry(cache, cwd) {
6754
+ const parsed = parseJsonFile(
6755
+ resolveLegacyFilePath(cache, cwd)
6756
+ );
6757
+ return parsed?.[cache.legacyEntryKey] ?? null;
6634
6758
  }
6635
- var LAST_HOST_FILE_NAME = "last-host.json";
6636
- var LAST_HOST_DIR_NAME = ".muggle-ai";
6637
- var LAST_HOST_VERSION = 1;
6638
- function readLastHost(cwd) {
6639
- const filePath = path9.join(cwd, LAST_HOST_DIR_NAME, LAST_HOST_FILE_NAME);
6759
+ function readCwdEntry(cache, cwd) {
6760
+ const cwdKey = path10.resolve(cwd);
6761
+ const entries = readHomeEntries(cache);
6762
+ const homeEntry = entries[cwdKey];
6763
+ if (homeEntry) {
6764
+ return homeEntry;
6765
+ }
6766
+ const legacyEntry = readLegacyEntry(cache, cwd);
6767
+ if (!legacyEntry) {
6768
+ return null;
6769
+ }
6640
6770
  try {
6641
- if (!fs5.existsSync(filePath)) {
6642
- return null;
6643
- }
6644
- const raw = JSON.parse(fs5.readFileSync(filePath, "utf-8"));
6645
- return raw.lastHost ?? null;
6771
+ writeHomeEntries(cache, { ...entries, [cwdKey]: legacyEntry });
6646
6772
  } catch (error) {
6647
- getLogger().warn("Failed to read last-host file", {
6648
- path: filePath,
6773
+ getLogger().warn("Failed to migrate cache entry to the home directory", {
6774
+ path: resolveHomeFilePath(cache),
6649
6775
  error: error instanceof Error ? error.message : String(error)
6650
6776
  });
6651
- return null;
6652
6777
  }
6778
+ return legacyEntry;
6779
+ }
6780
+ function writeCwdEntry(cache, cwd, entry) {
6781
+ const entries = readHomeEntries(cache);
6782
+ writeHomeEntries(cache, { ...entries, [path10.resolve(cwd)]: entry });
6783
+ }
6784
+ function clearCwdEntry(cache, cwd) {
6785
+ const entries = readHomeEntries(cache);
6786
+ const cwdKey = path10.resolve(cwd);
6787
+ if (cwdKey in entries) {
6788
+ delete entries[cwdKey];
6789
+ writeHomeEntries(cache, entries);
6790
+ }
6791
+ const legacyFilePath = resolveLegacyFilePath(cache, cwd);
6792
+ if (fs5.existsSync(legacyFilePath)) {
6793
+ fs5.unlinkSync(legacyFilePath);
6794
+ }
6795
+ }
6796
+
6797
+ // packages/mcps/src/shared/last-project-constants.ts
6798
+ var LAST_PROJECT_FILE_NAME = "last-project.json";
6799
+ var LAST_PROJECT_CACHE = {
6800
+ fileName: LAST_PROJECT_FILE_NAME,
6801
+ legacyEntryKey: "lastProject"
6802
+ };
6803
+
6804
+ // packages/mcps/src/shared/last-project.ts
6805
+ function readLastProject(cwd) {
6806
+ return readCwdEntry(LAST_PROJECT_CACHE, cwd);
6807
+ }
6808
+ function writeLastProject(cwd, lastProject) {
6809
+ writeCwdEntry(LAST_PROJECT_CACHE, cwd, {
6810
+ ...lastProject,
6811
+ savedAt: (/* @__PURE__ */ new Date()).toISOString()
6812
+ });
6813
+ }
6814
+ function clearLastProject(cwd) {
6815
+ clearCwdEntry(LAST_PROJECT_CACHE, cwd);
6816
+ }
6817
+
6818
+ // packages/mcps/src/shared/last-host-constants.ts
6819
+ var LAST_HOST_FILE_NAME = "last-host.json";
6820
+ var LAST_HOST_CACHE = {
6821
+ fileName: LAST_HOST_FILE_NAME,
6822
+ legacyEntryKey: "lastHost"
6823
+ };
6824
+
6825
+ // packages/mcps/src/shared/last-host.ts
6826
+ function readLastHost(cwd) {
6827
+ return readCwdEntry(LAST_HOST_CACHE, cwd);
6653
6828
  }
6654
6829
  function writeLastHost(cwd, host) {
6655
- const dir = path9.join(cwd, LAST_HOST_DIR_NAME);
6656
- fs5.mkdirSync(dir, { recursive: true });
6657
- const filePath = path9.join(dir, LAST_HOST_FILE_NAME);
6658
- const file = {
6659
- version: LAST_HOST_VERSION,
6660
- lastHost: { host, savedAt: (/* @__PURE__ */ new Date()).toISOString() }
6661
- };
6662
- fs5.writeFileSync(filePath, `${JSON.stringify(file, null, 2)}
6663
- `, "utf-8");
6830
+ writeCwdEntry(LAST_HOST_CACHE, cwd, {
6831
+ host,
6832
+ savedAt: (/* @__PURE__ */ new Date()).toISOString()
6833
+ });
6664
6834
  }
6665
6835
  function clearLastHost(cwd) {
6666
- const filePath = path9.join(cwd, LAST_HOST_DIR_NAME, LAST_HOST_FILE_NAME);
6667
- if (fs5.existsSync(filePath)) {
6668
- fs5.unlinkSync(filePath);
6669
- }
6836
+ clearCwdEntry(LAST_HOST_CACHE, cwd);
6670
6837
  }
6671
6838
 
6672
6839
  // packages/mcps/src/mcp/tools/local/tool-registry.ts
@@ -6765,42 +6932,42 @@ var runResultGetTool = {
6765
6932
  logger6.info("Executing muggle-local-run-result-get");
6766
6933
  const input = RunResultGetInputSchema.parse(ctx.input);
6767
6934
  const storage = getRunResultStorageService();
6768
- const result = storage.getRunResult(input.runId);
6769
- if (!result) {
6935
+ const storedRunResult = storage.getRunResult(input.runId);
6936
+ if (!storedRunResult) {
6770
6937
  return { content: `Run result not found: ${input.runId}`, isError: true };
6771
6938
  }
6772
6939
  const contentParts = [
6773
6940
  "## Run Result Details",
6774
6941
  "",
6775
- `**ID:** ${result.id}`,
6776
- `**Type:** ${result.runType}`,
6777
- `**Status:** ${result.status}`,
6778
- `**Cloud Test Case:** ${result.cloudTestCaseId}`,
6779
- `**Duration:** ${result.executionTimeMs ?? 0}ms`,
6780
- result.errorMessage ? `**Error:** ${result.errorMessage}` : "",
6942
+ `**ID:** ${storedRunResult.id}`,
6943
+ `**Type:** ${storedRunResult.runType}`,
6944
+ `**Status:** ${storedRunResult.status}`,
6945
+ `**Cloud Test Case:** ${storedRunResult.cloudTestCaseId}`,
6946
+ `**Duration:** ${storedRunResult.executionTimeMs ?? 0}ms`,
6947
+ storedRunResult.errorMessage ? `**Error:** ${storedRunResult.errorMessage}` : "",
6781
6948
  // The studio publishes the run during execution; these are its returned cloud refs.
6782
- result.viewUrl ? `**View URL:** ${result.viewUrl}` : "",
6783
- result.cloudTestScriptId ? `**Cloud Test Script ID:** ${result.cloudTestScriptId}` : "",
6784
- result.cloudActionScriptId ? `**Cloud Action Script ID:** ${result.cloudActionScriptId}` : ""
6949
+ storedRunResult.viewUrl ? `**View URL:** ${storedRunResult.viewUrl}` : "",
6950
+ storedRunResult.cloudTestScriptId ? `**Cloud Test Script ID:** ${storedRunResult.cloudTestScriptId}` : "",
6951
+ storedRunResult.cloudActionScriptId ? `**Cloud Action Script ID:** ${storedRunResult.cloudActionScriptId}` : ""
6785
6952
  ];
6786
6953
  let testScriptSteps;
6787
- if (result.testScriptId) {
6788
- const testScript = storage.getTestScript(result.testScriptId);
6954
+ if (storedRunResult.testScriptId) {
6955
+ const testScript = storage.getTestScript(storedRunResult.testScriptId);
6789
6956
  testScriptSteps = testScript?.actionScript?.length;
6790
6957
  }
6791
- if (result.artifactsDir && fs5.existsSync(result.artifactsDir)) {
6958
+ if (storedRunResult.artifactsDir && fs5.existsSync(storedRunResult.artifactsDir)) {
6792
6959
  contentParts.push(
6793
6960
  "",
6794
6961
  "### Artifacts (view action script + screenshots)",
6795
6962
  "",
6796
- `**Location:** \`${result.artifactsDir}\``,
6963
+ `**Location:** \`${storedRunResult.artifactsDir}\``,
6797
6964
  ""
6798
6965
  );
6799
- const actionScriptPath = path9.join(result.artifactsDir, "action-script.json");
6800
- const resultsMdPath = path9.join(result.artifactsDir, "results.md");
6801
- const screenshotsDir = path9.join(result.artifactsDir, "screenshots");
6802
- const stdoutLogPath = path9.join(result.artifactsDir, "stdout.log");
6803
- const stderrLogPath = path9.join(result.artifactsDir, "stderr.log");
6966
+ const actionScriptPath = path10.join(storedRunResult.artifactsDir, "action-script.json");
6967
+ const resultsMdPath = path10.join(storedRunResult.artifactsDir, "results.md");
6968
+ const screenshotsDir = path10.join(storedRunResult.artifactsDir, "screenshots");
6969
+ const stdoutLogPath = path10.join(storedRunResult.artifactsDir, "stdout.log");
6970
+ const stderrLogPath = path10.join(storedRunResult.artifactsDir, "stderr.log");
6804
6971
  const artifactItems = [];
6805
6972
  if (fs5.existsSync(actionScriptPath)) {
6806
6973
  artifactItems.push("- `action-script.json` \u2014 generated test steps");
@@ -6826,13 +6993,13 @@ var runResultGetTool = {
6826
6993
  "",
6827
6994
  "### Ending state",
6828
6995
  "",
6829
- `- **Status:** ${result.status}`,
6830
- `- **Duration:** ${result.executionTimeMs ?? 0}ms`,
6996
+ `- **Status:** ${storedRunResult.status}`,
6997
+ `- **Duration:** ${storedRunResult.executionTimeMs ?? 0}ms`,
6831
6998
  testScriptSteps !== void 0 ? `- **Steps generated:** ${testScriptSteps}` : "",
6832
- result.artifactsDir ? `- **Artifacts path:** \`${result.artifactsDir}\`` : ""
6999
+ storedRunResult.artifactsDir ? `- **Artifacts path:** \`${storedRunResult.artifactsDir}\`` : ""
6833
7000
  );
6834
7001
  const content = contentParts.filter(Boolean).join("\n");
6835
- return { content, isError: false, data: result };
7002
+ return { content, isError: false, data: storedRunResult };
6836
7003
  }
6837
7004
  };
6838
7005
  var testScriptListTool = {
@@ -6892,7 +7059,7 @@ var executeTestGenerationTool = {
6892
7059
  const input = ExecuteTestGenerationInputSchema.parse(ctx.input);
6893
7060
  const showUi = input.showUi !== false;
6894
7061
  try {
6895
- const result = await executeTestGeneration({
7062
+ const generationRun = await executeTestGeneration({
6896
7063
  testCase: input.testCase,
6897
7064
  localUrl: input.localUrl,
6898
7065
  cwd: input.cwd,
@@ -6902,19 +7069,19 @@ var executeTestGenerationTool = {
6902
7069
  freshSession: input.freshSession
6903
7070
  });
6904
7071
  const content = [
6905
- "## Test Generation " + (result.status === "passed" ? "Successful" : "Failed"),
7072
+ "## Test Generation " + (generationRun.status === "passed" ? "Successful" : "Failed"),
6906
7073
  "",
6907
- `**Run ID:** ${result.id}`,
6908
- `**Test Script ID:** ${result.testScriptId}`,
6909
- `**Status:** ${result.status}`,
6910
- `**Duration:** ${result.executionTimeMs}ms`,
7074
+ `**Run ID:** ${generationRun.id}`,
7075
+ `**Test Script ID:** ${generationRun.testScriptId}`,
7076
+ `**Status:** ${generationRun.status}`,
7077
+ `**Duration:** ${generationRun.executionTimeMs}ms`,
6911
7078
  `**UI:** ${showUi ? "visible GUI" : "headless"}`,
6912
- result.errorMessage ? `**Error:** ${result.errorMessage}` : ""
7079
+ generationRun.errorMessage ? `**Error:** ${generationRun.errorMessage}` : ""
6913
7080
  ].filter(Boolean).join("\n");
6914
7081
  return {
6915
7082
  content,
6916
- isError: result.status !== "passed",
6917
- data: result
7083
+ isError: generationRun.status !== "passed",
7084
+ data: generationRun
6918
7085
  };
6919
7086
  } catch (error) {
6920
7087
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -6933,7 +7100,7 @@ var executeReplayTool = {
6933
7100
  const input = ExecuteReplayInputSchema.parse(ctx.input);
6934
7101
  const showUi = input.showUi !== false;
6935
7102
  try {
6936
- const result = await executeReplay({
7103
+ const replayRun = await executeReplay({
6937
7104
  testScript: input.testScript,
6938
7105
  actionScript: input.actionScript,
6939
7106
  localUrl: input.localUrl,
@@ -6944,19 +7111,19 @@ var executeReplayTool = {
6944
7111
  freshSession: input.freshSession
6945
7112
  });
6946
7113
  const content = [
6947
- "## Test Replay " + (result.status === "passed" ? "Successful" : "Failed"),
7114
+ "## Test Replay " + (replayRun.status === "passed" ? "Successful" : "Failed"),
6948
7115
  "",
6949
- `**Run ID:** ${result.id}`,
6950
- `**Test Script ID:** ${result.testScriptId}`,
6951
- `**Status:** ${result.status}`,
6952
- `**Duration:** ${result.executionTimeMs}ms`,
7116
+ `**Run ID:** ${replayRun.id}`,
7117
+ `**Test Script ID:** ${replayRun.testScriptId}`,
7118
+ `**Status:** ${replayRun.status}`,
7119
+ `**Duration:** ${replayRun.executionTimeMs}ms`,
6953
7120
  `**UI:** ${showUi ? "visible GUI" : "headless"}`,
6954
- result.errorMessage ? `**Error:** ${result.errorMessage}` : ""
7121
+ replayRun.errorMessage ? `**Error:** ${replayRun.errorMessage}` : ""
6955
7122
  ].filter(Boolean).join("\n");
6956
7123
  return {
6957
7124
  content,
6958
- isError: result.status !== "passed",
6959
- data: result
7125
+ isError: replayRun.status !== "passed",
7126
+ data: replayRun
6960
7127
  };
6961
7128
  } catch (error) {
6962
7129
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -6982,27 +7149,22 @@ var cancelExecutionTool = {
6982
7149
  };
6983
7150
  var preferencesSetTool = {
6984
7151
  name: "muggle-local-preferences-set",
6985
- description: "Set a Muggle AI user preference. Preferences control automation behavior (auto-login, show browser, suggest test cases, etc.). Values: 'always' (proceed without asking), 'ask' (prompt each time), 'never' (skip without asking). Scope: 'global' writes to ~/.muggle-ai/preferences.json, 'project' writes to .muggle-ai/preferences.json in the repo root.",
7152
+ description: "Set a Muggle AI user preference. Preferences control automation behavior (auto-login, show browser, suggest test cases, etc.). Values: 'always' (proceed without asking), 'ask' (prompt each time), 'never' (skip without asking). Preferences are user-level: they always write to ~/.muggle-ai/preferences.json and apply to every repo.",
6986
7153
  inputSchema: PreferencesSetInputSchema,
6987
7154
  execute: async (ctx) => {
6988
7155
  const logger6 = createChildLogger2(ctx.correlationId);
6989
7156
  logger6.info("Executing muggle-local-preferences-set");
6990
7157
  const input = PreferencesSetInputSchema.parse(ctx.input);
6991
- writePreferences2(
6992
- { [input.key]: input.value },
6993
- input.scope,
6994
- void 0,
6995
- input.cwd
6996
- );
7158
+ writePreferences2({ [input.key]: input.value });
6997
7159
  return {
6998
- content: `**${input.key}** set to **${input.value}** (${input.scope}).`,
7160
+ content: `**${input.key}** set to **${input.value}**.`,
6999
7161
  isError: false
7000
7162
  };
7001
7163
  }
7002
7164
  };
7003
7165
  var lastProjectGetTool = {
7004
7166
  name: "muggle-local-last-project-get",
7005
- 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.",
7167
+ description: `Get the cached last-used Muggle Test project for a repo (read from the entry keyed on <cwd> in ${MUGGLE_HOME_DISPLAY_DIR}/${LAST_PROJECT_FILE_NAME}). 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.`,
7006
7168
  inputSchema: LastProjectGetInputSchema,
7007
7169
  execute: async (ctx) => {
7008
7170
  const logger6 = createChildLogger2(ctx.correlationId);
@@ -7013,7 +7175,7 @@ var lastProjectGetTool = {
7013
7175
  const content2 = [
7014
7176
  "No cached last-used project for this repo.",
7015
7177
  "",
7016
- `Looked at: \`${input.cwd}/.muggle-ai/${LAST_PROJECT_FILE_NAME}\``,
7178
+ `Looked at: \`${MUGGLE_HOME_DISPLAY_DIR}/${LAST_PROJECT_FILE_NAME}\` (entry for \`${input.cwd}\`)`,
7017
7179
  "",
7018
7180
  "The cache is populated when a user picks an existing project AND chooses 'Yes, save it' on the memory picker (the autoSelectProject preference)."
7019
7181
  ].join("\n");
@@ -7032,7 +7194,7 @@ var lastProjectGetTool = {
7032
7194
  };
7033
7195
  var lastProjectSetTool = {
7034
7196
  name: "muggle-local-last-project-set",
7035
- 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'.",
7197
+ description: `Save the user's selected Muggle Test project as the cached last-used project for this repo. Writes the entry keyed on <cwd> in ${MUGGLE_HOME_DISPLAY_DIR}/${LAST_PROJECT_FILE_NAME}. 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'.`,
7036
7198
  inputSchema: LastProjectSetInputSchema,
7037
7199
  execute: async (ctx) => {
7038
7200
  const logger6 = createChildLogger2(ctx.correlationId);
@@ -7046,7 +7208,7 @@ var lastProjectSetTool = {
7046
7208
  const content = [
7047
7209
  `Cached **${input.projectName}** as the last-used project for this repo.`,
7048
7210
  "",
7049
- `Written to: \`${input.cwd}/.muggle-ai/${LAST_PROJECT_FILE_NAME}\``,
7211
+ `Written to: \`${MUGGLE_HOME_DISPLAY_DIR}/${LAST_PROJECT_FILE_NAME}\` (entry for \`${input.cwd}\`)`,
7050
7212
  "",
7051
7213
  "Skills will silently reuse this project on future runs when `autoSelectProject = always`."
7052
7214
  ].join("\n");
@@ -7065,14 +7227,14 @@ var lastProjectClearTool = {
7065
7227
  const content = [
7066
7228
  "Cleared the cached last-used project for this repo.",
7067
7229
  "",
7068
- `Path: \`${input.cwd}/.muggle-ai/${LAST_PROJECT_FILE_NAME}\``
7230
+ `Path: \`${MUGGLE_HOME_DISPLAY_DIR}/${LAST_PROJECT_FILE_NAME}\` (entry for \`${input.cwd}\`)`
7069
7231
  ].join("\n");
7070
7232
  return { content, isError: false };
7071
7233
  }
7072
7234
  };
7073
7235
  var lastHostGetTool = {
7074
7236
  name: "muggle-local-last-host-get",
7075
- 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.",
7237
+ description: `Get the cached last-used local dev server URL for a repo (read from the entry keyed on <cwd> in ${MUGGLE_HOME_DISPLAY_DIR}/${LAST_HOST_FILE_NAME}). 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.`,
7076
7238
  inputSchema: LastHostGetInputSchema,
7077
7239
  execute: async (ctx) => {
7078
7240
  const logger6 = createChildLogger2(ctx.correlationId);
@@ -7083,7 +7245,7 @@ var lastHostGetTool = {
7083
7245
  const content2 = [
7084
7246
  "No cached last-used host for this repo.",
7085
7247
  "",
7086
- `Looked at: \`${input.cwd}/.muggle-ai/${LAST_HOST_FILE_NAME}\``
7248
+ `Looked at: \`${MUGGLE_HOME_DISPLAY_DIR}/${LAST_HOST_FILE_NAME}\` (entry for \`${input.cwd}\`)`
7087
7249
  ].join("\n");
7088
7250
  return { content: content2, isError: false };
7089
7251
  }
@@ -7098,7 +7260,7 @@ var lastHostGetTool = {
7098
7260
  };
7099
7261
  var lastHostSetTool = {
7100
7262
  name: "muggle-local-last-host-set",
7101
- 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.",
7263
+ description: `Save the user's chosen local dev server URL as the cached last-used host for this repo. Writes the entry keyed on <cwd> in ${MUGGLE_HOME_DISPLAY_DIR}/${LAST_HOST_FILE_NAME}. 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.`,
7102
7264
  inputSchema: LastHostSetInputSchema,
7103
7265
  execute: async (ctx) => {
7104
7266
  const logger6 = createChildLogger2(ctx.correlationId);
@@ -7208,7 +7370,7 @@ async function executeTool(name, input, correlationId) {
7208
7370
  props: { toolName: name, toolSurface: ToolSurface.Local, correlationId }
7209
7371
  });
7210
7372
  try {
7211
- const result = await tool.execute({ input, correlationId });
7373
+ const toolResult = await tool.execute({ input, correlationId });
7212
7374
  safeTrack2({
7213
7375
  name: EventName.McpToolCompleted,
7214
7376
  props: {
@@ -7216,10 +7378,10 @@ async function executeTool(name, input, correlationId) {
7216
7378
  toolSurface: ToolSurface.Local,
7217
7379
  correlationId,
7218
7380
  durationMs: Date.now() - startTime,
7219
- outcome: result.isError ? Outcome.Error : Outcome.Success
7381
+ outcome: toolResult.isError ? Outcome.Error : Outcome.Success
7220
7382
  }
7221
7383
  });
7222
- return result;
7384
+ return toolResult;
7223
7385
  } catch (err) {
7224
7386
  safeTrack2({
7225
7387
  name: EventName.McpToolCompleted,
@@ -7463,4 +7625,12 @@ var plugins_exports = {};
7463
7625
  // packages/mcps/src/mcp/agents/index.ts
7464
7626
  var agents_exports = {};
7465
7627
 
7466
- 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, getDisclosureCopy, getDownloadBaseUrl, getElectronAppChecksums, getElectronAppDir, getElectronAppVersion, getElectronAppVersionSource, getLocalQaTools, getLogger, getPlatformKey, getQaTools, getValidApiKeyData, getValidCredentials, hasApiKey, hasShownDisclosure, initTelemetry, isElectronAppInstalled, isFirstRun, loadApiKeyData, loadCredentials, local_exports2 as local_exports, markDisclosureShown, mcp_exports, openBrowserUrl, performLogin, performLogout, pollDeviceCode, readGlobalPreferences, readProjectPreferences, resetConfig, resetLogger, resetPreference, resolveElectronAppPathOrNull, resolvePreferences, saveApiKey, saveApiKeyData, saveCredentials, src_exports, startDeviceCodeFlow, toolRequiresAuth, track, validatePreference, verifyFileChecksum, writePreferences2 as writePreferences };
7628
+ // packages/mcps/src/shared/watcher-lifetime-constants.ts
7629
+ var WATCHER_LIFETIME_UNBOUNDED_SECONDS = 0;
7630
+ var WATCHER_LIFETIME_SECONDS = {
7631
+ ["1d" /* OneDay */]: 86400,
7632
+ ["7d" /* SevenDays */]: 604800,
7633
+ ["never" /* Never */]: WATCHER_LIFETIME_UNBOUNDED_SECONDS
7634
+ };
7635
+
7636
+ export { DEFAULT_PREFERENCES, EventName, PREFERENCES_FILE_NAME, PREFERENCES_PROJECT_DIR_NAME, PREFERENCES_SCHEMA, PREFERENCES_VERSION, PREFERENCE_ALLOWED_VALUES, PreferenceKey, PreferenceValue, ProjectPreferencesReconcileOutcome, ServiceName, Surface, WATCHER_LIFETIME_SECONDS, WATCHER_LIFETIME_UNBOUNDED_SECONDS, __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, getDisclosureCopy, getDownloadBaseUrl, getElectronAppChecksums, getElectronAppDir, getElectronAppVersion, getElectronAppVersionSource, getLocalQaTools, getLogger, getPlatformKey, getQaTools, getValidApiKeyData, getValidCredentials, hasApiKey, hasShownDisclosure, initTelemetry, isElectronAppInstalled, isFirstRun, loadApiKeyData, loadCredentials, local_exports2 as local_exports, markDisclosureShown, mcp_exports, openBrowserUrl, performLogin, performLogout, pollDeviceCode, reconcileProjectPreferences, resetConfig, resetLogger, resetPreference, resolveElectronAppPathOrNull, resolvePreferences, saveApiKey, saveApiKeyData, saveCredentials, src_exports, startDeviceCodeFlow, toolRequiresAuth, track, validatePreference, verifyFileChecksum, writePreferences2 as writePreferences };