@muggleai/works 5.27.0 → 5.28.0-staging.250
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/dist/{chunk-6BAB7IVG.js → chunk-HBCASFGN.js} +300 -99
- package/dist/{chunk-B5BWBZZ5.js → chunk-KGNRBL52.js} +46 -58
- package/dist/cli.js +2 -2
- package/dist/index.js +2 -2
- package/dist/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/plugin/.codex-mcp.json +1 -1
- package/dist/plugin/.codex-plugin/plugin.json +1 -1
- package/dist/plugin/.cursor-plugin/plugin.json +1 -1
- package/dist/plugin/.mcp.json +1 -1
- package/dist/plugin/skills/_shared/dev-loop/run.md +1 -1
- package/dist/plugin/skills/do/pre-flight.md +1 -1
- package/dist/plugin/skills/muggle-test/execute-local.md +8 -6
- package/dist/plugin/skills/muggle-test-feature-local/SKILL.md +4 -2
- package/dist/plugin/skills/muggle-test-prepare/SKILL.md +7 -4
- package/dist/plugin/skills/muggle-test-prepare/steps/claim-stack.md +10 -4
- package/dist/release-manifest.json +3 -3
- package/dist/src-ZOCXV6PT.js +1 -0
- package/package.json +2 -2
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-mcp.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/.cursor-plugin/plugin.json +1 -1
- package/plugin/.mcp.json +1 -1
- package/plugin/skills/_shared/dev-loop/run.md +1 -1
- package/plugin/skills/do/pre-flight.md +1 -1
- package/plugin/skills/muggle-test/execute-local.md +8 -6
- package/plugin/skills/muggle-test-feature-local/SKILL.md +4 -2
- package/plugin/skills/muggle-test-prepare/SKILL.md +7 -4
- package/plugin/skills/muggle-test-prepare/steps/claim-stack.md +10 -4
- package/dist/src-MCJFH7AB.js +0 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { __export, getLogger, getConfig, createChildLogger, buildElectronAppReleaseAssetUrl, getActiveRuntimeTarget, getAuthService, hasApiKey, getElectronAppVersion, getElectronAppDir, getPlatformKey, reconcileProjectPreferences, getDataDir2, PREFERENCES_FILE_NAME, isFirstRun, writePreferences, DEFAULT_PREFERENCES, isElectronAppInstalled, getElectronAppChecksums, getChecksumForPlatform, getElectronAppSignedFromVersion, getReleaseSignerIdentityUri, verifyFileChecksum, resolveElectronAppExecutablePath, calculateFileChecksum, initTelemetry, Surface, ServiceName, track, EventName, getQaTools, getLocalQaTools, performLogout, assertDeviceCodeClientProvisioned, performLogin, getActiveElectronAppReleaseStream, buildToolInputJsonSchema, toolRequiresAuth, getCallerCredentials, parseElectronAppDirName, getElectronAppReleaseTagPrefix, hasShownDisclosure, markDisclosureShown, getBundledElectronAppVersion, getElectronAppVersionSource, getCredentialsFilePath, ElectronAppReleaseStream, buildElectronAppChecksumsUrl, __require, getDisclosureCopy, buildOnboardingPlan, applyOnboardingAnswers, needsOnboarding, buildLlmSourcePlan, describeLlmSourceStatus, resolveLlmSourceStatus, readRunView, RunViewError, renderRunViewText, writeRunViewHtml, openBrowserUrl, evaluateCheckoutFreshness, runGitCommand, BLOCKING_FRESHNESS_VERDICTS, claimServiceStack, BUSY_EXIT_CODE, checkServiceStack, releaseServiceStack, readServiceStackStatus, resolvePreferences, OnboardingBlanketChoice, PreferenceKey, LlmSource, applyLlmSourceSelection, describeStackMismatch, getDataDir, DEFAULT_LOCAL_LLM_BASE_URL, isValidLocalBaseUrl, local_exports } from './chunk-
|
|
1
|
+
import { __export, getLogger, getConfig, createChildLogger, buildElectronAppReleaseAssetUrl, getActiveRuntimeTarget, getAuthService, hasApiKey, getElectronAppVersion, getElectronAppDir, getPlatformKey, reconcileProjectPreferences, getDataDir2, PREFERENCES_FILE_NAME, isFirstRun, writePreferences, DEFAULT_PREFERENCES, isElectronAppInstalled, getElectronAppChecksums, getChecksumForPlatform, getElectronAppSignedFromVersion, getReleaseSignerIdentityUri, verifyFileChecksum, resolveElectronAppExecutablePath, calculateFileChecksum, initTelemetry, Surface, ServiceName, track, EventName, getQaTools, getLocalQaTools, performLogout, assertDeviceCodeClientProvisioned, performLogin, getActiveElectronAppReleaseStream, buildToolInputJsonSchema, toolRequiresAuth, getCallerCredentials, parseElectronAppDirName, getElectronAppReleaseTagPrefix, hasShownDisclosure, markDisclosureShown, getBundledElectronAppVersion, getElectronAppVersionSource, getCredentialsFilePath, ElectronAppReleaseStream, buildElectronAppChecksumsUrl, __require, getDisclosureCopy, buildOnboardingPlan, applyOnboardingAnswers, needsOnboarding, buildLlmSourcePlan, describeLlmSourceStatus, resolveLlmSourceStatus, readRunView, RunViewError, renderRunViewText, writeRunViewHtml, openBrowserUrl, evaluateCheckoutFreshness, runGitCommand, BLOCKING_FRESHNESS_VERDICTS, claimServiceStack, BUSY_EXIT_CODE, checkServiceStack, releaseServiceStack, readServiceStackStatus, resolvePreferences, OnboardingBlanketChoice, PreferenceKey, LlmSource, applyLlmSourceSelection, describeStackMismatch, getDataDir, DEFAULT_LOCAL_LLM_BASE_URL, isValidLocalBaseUrl, local_exports } from './chunk-HBCASFGN.js';
|
|
2
2
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
3
|
import { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
4
4
|
import { v4 } from 'uuid';
|
|
5
5
|
import { z, ZodError } from 'zod';
|
|
6
6
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
7
7
|
import * as fs from 'fs';
|
|
8
|
-
import { readFileSync, existsSync, rmSync, mkdirSync, readdirSync, createWriteStream, writeFileSync, statSync, readlinkSync, realpathSync, openSync, writeSync, closeSync, unlinkSync, copyFileSync, renameSync, appendFileSync, readSync
|
|
8
|
+
import { readFileSync, existsSync, rmSync, mkdirSync, readdirSync, createWriteStream, writeFileSync, statSync, readlinkSync, realpathSync, openSync, writeSync, closeSync, unlinkSync, copyFileSync, renameSync, appendFileSync, readSync } from 'fs';
|
|
9
9
|
import * as path from 'path';
|
|
10
10
|
import { dirname, resolve, join, basename } from 'path';
|
|
11
11
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
@@ -1151,7 +1151,7 @@ async function resolveGsScreenshotUrls(report, opts) {
|
|
|
1151
1151
|
if (gsUrls.length === 0) {
|
|
1152
1152
|
return report;
|
|
1153
1153
|
}
|
|
1154
|
-
const mcps = await import('./src-
|
|
1154
|
+
const mcps = await import('./src-ZOCXV6PT.js');
|
|
1155
1155
|
const credentials = await mcps.getCallerCredentialsAsync();
|
|
1156
1156
|
if (!credentials.bearerToken && !credentials.apiKey) {
|
|
1157
1157
|
stderrWrite(
|
|
@@ -2400,8 +2400,6 @@ var TERMINAL_PR_RECHECK_MS = 6 * 60 * 6e4;
|
|
|
2400
2400
|
var WATCH_COMMAND_TIMEOUT_MS = 3e4;
|
|
2401
2401
|
var WATCH_COMMAND_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
|
|
2402
2402
|
var SESSION_FILE_HEAD_BYTES = 64 * 1024;
|
|
2403
|
-
var SESSION_FILE_TAIL_BYTES = 64 * 1024;
|
|
2404
|
-
var USAGE_LIMIT_NOTICE_PATTERN = /\bhit your (?:session|usage|weekly|daily) limit\b|\busage limit reached\b/i;
|
|
2405
2403
|
var WATCH_LOCK_FILE = "watch.lock";
|
|
2406
2404
|
var WATCH_LOCK_WAIT_MS = 6e4;
|
|
2407
2405
|
var PR_WATCH_SLOTS_RELATIVE_PATH = ["muggle-do", "sessions"];
|
|
@@ -2434,6 +2432,8 @@ var PR_WATCH_LOOP_PATTERN = /pr-watch-loop\.sh\b.*?--slot\s+(?:"([^"]+)"|(\S+))/
|
|
|
2434
2432
|
var CLAUDE_SESSION_REGISTRY_DIR_NAME = "sessions";
|
|
2435
2433
|
var CLAUDE_PROJECTS_DIR_NAME = "projects";
|
|
2436
2434
|
var CLAUDE_INSTALLED_PLUGINS_RELATIVE_PATH = ["plugins", "installed_plugins.json"];
|
|
2435
|
+
var CLAUDE_GLOBAL_CONFIG_FILE_NAME = ".claude.json";
|
|
2436
|
+
var USAGE_WINDOW_EXHAUSTED_PERCENT = 100;
|
|
2437
2437
|
var RELOAD_PLUGIN_SCOPE = "user";
|
|
2438
2438
|
var MUGGLE_PLUGIN_ID = "muggleai@muggle-works";
|
|
2439
2439
|
var CODEX_ROLLOUT_PREFIX = "rollout-";
|
|
@@ -2487,6 +2487,7 @@ function resolveWatchPaths(env = process.env) {
|
|
|
2487
2487
|
claudeSessionRegistryDir: join(claudeConfigDir, CLAUDE_SESSION_REGISTRY_DIR_NAME),
|
|
2488
2488
|
claudeProjectsDir: join(claudeConfigDir, CLAUDE_PROJECTS_DIR_NAME),
|
|
2489
2489
|
claudeInstalledPluginsFile: join(claudeConfigDir, ...CLAUDE_INSTALLED_PLUGINS_RELATIVE_PATH),
|
|
2490
|
+
claudeGlobalConfigFile: join(env.CLAUDE_CONFIG_DIR || homedir(), CLAUDE_GLOBAL_CONFIG_FILE_NAME),
|
|
2490
2491
|
codexSessionsDir: join(codexHome, "sessions"),
|
|
2491
2492
|
muggleInstallLogFile: join(muggleDataDir, MUGGLE_INSTALL_LOG_FILE)
|
|
2492
2493
|
};
|
|
@@ -2542,14 +2543,14 @@ function renderSystemdService(job) {
|
|
|
2542
2543
|
].join("\n");
|
|
2543
2544
|
}
|
|
2544
2545
|
function renderSystemdTimer(job) {
|
|
2545
|
-
const
|
|
2546
|
+
const minutes2 = Math.max(1, Math.round(job.intervalSeconds / 60));
|
|
2546
2547
|
return [
|
|
2547
2548
|
"[Unit]",
|
|
2548
|
-
`Description=Run the Muggle watch every ${
|
|
2549
|
+
`Description=Run the Muggle watch every ${minutes2} min`,
|
|
2549
2550
|
"",
|
|
2550
2551
|
"[Timer]",
|
|
2551
|
-
`OnActiveSec=${
|
|
2552
|
-
`OnUnitActiveSec=${
|
|
2552
|
+
`OnActiveSec=${minutes2}min`,
|
|
2553
|
+
`OnUnitActiveSec=${minutes2}min`,
|
|
2553
2554
|
`Unit=${WATCH_SYSTEMD_UNIT_NAME}.service`,
|
|
2554
2555
|
"",
|
|
2555
2556
|
"[Install]",
|
|
@@ -2558,9 +2559,9 @@ function renderSystemdTimer(job) {
|
|
|
2558
2559
|
].join("\n");
|
|
2559
2560
|
}
|
|
2560
2561
|
function renderCronLine(job) {
|
|
2561
|
-
const
|
|
2562
|
+
const minutes2 = Math.max(1, Math.round(job.intervalSeconds / 60));
|
|
2562
2563
|
const command = launcherArgs(job).map(shellQuote).join(" ").replace(/%/g, "\\%");
|
|
2563
|
-
return `*/${
|
|
2564
|
+
return `*/${minutes2} * * * * ${command} ${WATCH_CRON_MARKER}`;
|
|
2564
2565
|
}
|
|
2565
2566
|
function renderWatchEnvFile(pathValue) {
|
|
2566
2567
|
return [
|
|
@@ -4092,6 +4093,11 @@ The archive may be corrupted or in an unexpected format.`
|
|
|
4092
4093
|
|
|
4093
4094
|
// src/cli/service-stack.ts
|
|
4094
4095
|
var NEED_PATTERN = /^(\d+)=(.+?)(?:@([0-9a-f]{7,40}))?$/i;
|
|
4096
|
+
var FAILING_CHECK_VERDICTS = /* @__PURE__ */ new Set([
|
|
4097
|
+
"STALE" /* Stale */,
|
|
4098
|
+
"PEER_LEASE" /* PeerLease */,
|
|
4099
|
+
"ENDED" /* Ended */
|
|
4100
|
+
]);
|
|
4095
4101
|
function parseStackNeed(value) {
|
|
4096
4102
|
const match = NEED_PATTERN.exec(value.trim());
|
|
4097
4103
|
if (!match) throw new Error(`--need "${value}" is not <port>=<run-dir>[@<sha>]`);
|
|
@@ -4135,7 +4141,8 @@ function renderStackStatusSummary(report) {
|
|
|
4135
4141
|
}
|
|
4136
4142
|
report.queue.forEach((ticket, index) => {
|
|
4137
4143
|
const ports = ticket.needs.map((need) => need.port).join(", ");
|
|
4138
|
-
|
|
4144
|
+
const rank = ticket.pullRequest ? `PR #${ticket.pullRequest.number} opened ${minutes(now - ticket.pullRequest.createdAt)} ago` : "no open PR";
|
|
4145
|
+
lines.push(`waiting ${index + 1}: ${ticket.label ?? ticket.cwd} \u2014 port ${ports}, ${rank} (asked ${seconds(now - ticket.lastSeenAt)} ago)`);
|
|
4139
4146
|
});
|
|
4140
4147
|
for (const service of report.services) {
|
|
4141
4148
|
lines.push(`service: ${service.port} ${service.name} \u2014 ${service.dir} @ ${shortSha2(service.headSha)} (PID ${service.pid})`);
|
|
@@ -4159,7 +4166,7 @@ async function stackCheckCommand(flags) {
|
|
|
4159
4166
|
port: flags.port === void 0 ? void 0 : Number(flags.port)
|
|
4160
4167
|
});
|
|
4161
4168
|
writeReport({ json: flags.json, report, summary: renderStackCheckSummary(report) });
|
|
4162
|
-
if (
|
|
4169
|
+
if (FAILING_CHECK_VERDICTS.has(report.verdict)) process.exitCode = 1;
|
|
4163
4170
|
}
|
|
4164
4171
|
async function stackReleaseCommand(flags) {
|
|
4165
4172
|
const report = await releaseServiceStack({ cwd: flags.cwd ?? process.cwd() });
|
|
@@ -4184,6 +4191,9 @@ function shortSha2(sha) {
|
|
|
4184
4191
|
function seconds(ms) {
|
|
4185
4192
|
return `${Math.max(0, Math.round(ms / 1e3))}s`;
|
|
4186
4193
|
}
|
|
4194
|
+
function minutes(ms) {
|
|
4195
|
+
return `${Math.max(0, Math.round(ms / 6e4))}m`;
|
|
4196
|
+
}
|
|
4187
4197
|
var logger7 = getLogger();
|
|
4188
4198
|
var GITHUB_RELEASES_API = "https://api.github.com/repos/multiplex-ai/muggle-ai-works/releases?per_page=100";
|
|
4189
4199
|
var INSTALL_METADATA_FILE_NAME2 = ".install-metadata.json";
|
|
@@ -4629,21 +4639,6 @@ function readFileHead(filePath) {
|
|
|
4629
4639
|
if (fileDescriptor !== void 0) closeSync(fileDescriptor);
|
|
4630
4640
|
}
|
|
4631
4641
|
}
|
|
4632
|
-
function readFileTail(filePath) {
|
|
4633
|
-
let fileDescriptor;
|
|
4634
|
-
try {
|
|
4635
|
-
fileDescriptor = openSync(filePath, "r");
|
|
4636
|
-
const fileSize = fstatSync(fileDescriptor).size;
|
|
4637
|
-
const length = Math.min(fileSize, SESSION_FILE_TAIL_BYTES);
|
|
4638
|
-
const buffer = Buffer.alloc(length);
|
|
4639
|
-
const bytesRead = readSync(fileDescriptor, buffer, 0, length, fileSize - length);
|
|
4640
|
-
return buffer.subarray(0, bytesRead).toString("utf-8");
|
|
4641
|
-
} catch {
|
|
4642
|
-
return void 0;
|
|
4643
|
-
} finally {
|
|
4644
|
-
if (fileDescriptor !== void 0) closeSync(fileDescriptor);
|
|
4645
|
-
}
|
|
4646
|
-
}
|
|
4647
4642
|
function cwdFromSessionHead(head) {
|
|
4648
4643
|
const parts = head.match(/"cwd"\s*:\s*("(?:[^"\\]|\\.)*")/);
|
|
4649
4644
|
if (!parts) return void 0;
|
|
@@ -4738,36 +4733,12 @@ function findTranscriptPath(projectsDir, sessionId) {
|
|
|
4738
4733
|
}
|
|
4739
4734
|
return void 0;
|
|
4740
4735
|
}
|
|
4741
|
-
function assistantText(entry) {
|
|
4742
|
-
if (entry.type !== "assistant") return void 0;
|
|
4743
|
-
const content = entry.message?.content;
|
|
4744
|
-
if (typeof content === "string") return content;
|
|
4745
|
-
if (!Array.isArray(content)) return "";
|
|
4746
|
-
return content.map((part) => typeof part?.text === "string" ? part.text : "").join(" ");
|
|
4747
|
-
}
|
|
4748
4736
|
function findClaudeSessionCwd(projectsDir, sessionId) {
|
|
4749
4737
|
const transcriptPath = findTranscriptPath(projectsDir, sessionId);
|
|
4750
4738
|
if (transcriptPath === void 0) return void 0;
|
|
4751
4739
|
const head = readFileHead(transcriptPath);
|
|
4752
4740
|
return head === void 0 ? void 0 : cwdFromSessionHead(head);
|
|
4753
4741
|
}
|
|
4754
|
-
function isStoppedAtUsageLimit(projectsDir, sessionId) {
|
|
4755
|
-
const transcriptPath = findTranscriptPath(projectsDir, sessionId);
|
|
4756
|
-
const tail = transcriptPath === void 0 ? void 0 : readFileTail(transcriptPath);
|
|
4757
|
-
if (tail === void 0) return false;
|
|
4758
|
-
const lines = tail.split("\n");
|
|
4759
|
-
for (let index = lines.length - 1; index >= 0; index--) {
|
|
4760
|
-
let entry;
|
|
4761
|
-
try {
|
|
4762
|
-
entry = JSON.parse(lines[index]);
|
|
4763
|
-
} catch {
|
|
4764
|
-
continue;
|
|
4765
|
-
}
|
|
4766
|
-
const text = assistantText(entry);
|
|
4767
|
-
if (text !== void 0) return USAGE_LIMIT_NOTICE_PATTERN.test(text);
|
|
4768
|
-
}
|
|
4769
|
-
return false;
|
|
4770
|
-
}
|
|
4771
4742
|
var ROLLOUT_SESSION_ID = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/i;
|
|
4772
4743
|
function sortedChildDirsDescending(dir) {
|
|
4773
4744
|
try {
|
|
@@ -5453,8 +5424,8 @@ function readWindowsProcessTable(runCommand, nowMs) {
|
|
|
5453
5424
|
function parsePsElapsedSeconds(elapsed) {
|
|
5454
5425
|
const parts = elapsed.match(/^(?:(\d+)-)?(?:(\d+):)?(\d+):(\d+)$/);
|
|
5455
5426
|
if (!parts) return void 0;
|
|
5456
|
-
const [, days, hours,
|
|
5457
|
-
return Number(days ?? 0) * 86400 + Number(hours ?? 0) * 3600 + Number(
|
|
5427
|
+
const [, days, hours, minutes2, seconds2] = parts;
|
|
5428
|
+
return Number(days ?? 0) * 86400 + Number(hours ?? 0) * 3600 + Number(minutes2) * 60 + Number(seconds2);
|
|
5458
5429
|
}
|
|
5459
5430
|
function executableNameOf(commandLine) {
|
|
5460
5431
|
const firstToken = commandLine.trim().split(/\s+/)[0] ?? "";
|
|
@@ -5507,7 +5478,7 @@ function findRunningOwner(sessionsById, ownerSessionId) {
|
|
|
5507
5478
|
const ownerShortId = shortSessionId(ownerSessionId);
|
|
5508
5479
|
return sessionsById.get(ownerSessionId) ?? [...sessionsById.values()].find((session) => session.shortId === ownerShortId);
|
|
5509
5480
|
}
|
|
5510
|
-
function planWatcherRecoveries(assessments, attemptsByOwner, nowMs, isPollingDisabled, sessionsById = /* @__PURE__ */ new Map(),
|
|
5481
|
+
function planWatcherRecoveries(assessments, attemptsByOwner, nowMs, isPollingDisabled, sessionsById = /* @__PURE__ */ new Map(), isUsageLimitInForce = false) {
|
|
5511
5482
|
if (isPollingDisabled) return [];
|
|
5512
5483
|
const slugsByOwner = /* @__PURE__ */ new Map();
|
|
5513
5484
|
for (const assessment of assessments) {
|
|
@@ -5524,9 +5495,8 @@ function planWatcherRecoveries(assessments, attemptsByOwner, nowMs, isPollingDis
|
|
|
5524
5495
|
const recentAttemptsMs = (attemptsByOwner[ownerSessionId] ?? []).map((attemptedAt) => Date.parse(attemptedAt)).filter((attemptedAtMs) => !Number.isNaN(attemptedAtMs) && nowMs - attemptedAtMs < RECOVERY_ATTEMPT_WINDOW_MS);
|
|
5525
5496
|
const lastAttemptMs = Math.max(-Infinity, ...recentAttemptsMs);
|
|
5526
5497
|
const runningOwner = findRunningOwner(sessionsById, ownerSessionId);
|
|
5527
|
-
const isRunningAtUsageLimit = runningOwner !== void 0 && [ownerSessionId, runningOwner.sessionId].some((sessionId) => isStoppedAtUsageLimit2(sessionId));
|
|
5528
5498
|
let outcome = "resume-due" /* ResumeDue */;
|
|
5529
|
-
if (
|
|
5499
|
+
if (isUsageLimitInForce) outcome = "awaiting-usage-limit-reset" /* AwaitingUsageLimitReset */;
|
|
5530
5500
|
else if (recentAttemptsMs.length >= RECOVERY_MAX_ATTEMPTS) outcome = "attempts-exhausted" /* AttemptsExhausted */;
|
|
5531
5501
|
else if (nowMs - lastAttemptMs < RECOVERY_COOLDOWN_MS) outcome = "cooling-down" /* CoolingDown */;
|
|
5532
5502
|
else if (resumesPlanned >= MAX_RESUMES_PER_TICK) outcome = "deferred-by-tick-cap" /* DeferredByTickCap */;
|
|
@@ -5786,6 +5756,24 @@ function classifyTrackedProcesses(table, platform4) {
|
|
|
5786
5756
|
}
|
|
5787
5757
|
return tracked;
|
|
5788
5758
|
}
|
|
5759
|
+
function isUsageWindowExhausted(window, nowMs) {
|
|
5760
|
+
if (typeof window !== "object" || window === null) return false;
|
|
5761
|
+
const { utilization, resets_at: resetsAt, locked_reason: lockedReason } = window;
|
|
5762
|
+
const resetsAtMs = typeof resetsAt === "string" ? Date.parse(resetsAt) : Number.NaN;
|
|
5763
|
+
if (Number.isNaN(resetsAtMs) || resetsAtMs <= nowMs) return false;
|
|
5764
|
+
const isUtilizationExhausted = typeof utilization === "number" && utilization >= USAGE_WINDOW_EXHAUSTED_PERCENT;
|
|
5765
|
+
return isUtilizationExhausted || lockedReason !== null && lockedReason !== void 0;
|
|
5766
|
+
}
|
|
5767
|
+
function isClaudeUsageLimitInForce(claudeGlobalConfigFile, nowMs) {
|
|
5768
|
+
let usageWindows;
|
|
5769
|
+
try {
|
|
5770
|
+
const config = JSON.parse(readFileSync(claudeGlobalConfigFile, "utf-8"));
|
|
5771
|
+
usageWindows = config.cachedUsageUtilization?.utilization ?? {};
|
|
5772
|
+
} catch {
|
|
5773
|
+
return false;
|
|
5774
|
+
}
|
|
5775
|
+
return Object.values(usageWindows).some((usageWindow) => isUsageWindowExhausted(usageWindow, nowMs));
|
|
5776
|
+
}
|
|
5789
5777
|
function readJsonFile(filePath) {
|
|
5790
5778
|
try {
|
|
5791
5779
|
return JSON.parse(readFileSync(filePath, "utf-8"));
|
|
@@ -6046,7 +6034,7 @@ function runTickUnlocked(deps) {
|
|
|
6046
6034
|
deps.nowMs,
|
|
6047
6035
|
isPollingDisabled,
|
|
6048
6036
|
new Map(runningSessions.map((session) => [session.sessionId, session])),
|
|
6049
|
-
(
|
|
6037
|
+
isClaudeUsageLimitInForce(deps.paths.claudeGlobalConfigFile, deps.nowMs)
|
|
6050
6038
|
);
|
|
6051
6039
|
const executed = executeWatcherRecoveries(planned, recoveryAttemptsByOwner, {
|
|
6052
6040
|
runCommand: deps.runCommand,
|
package/dist/cli.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { src_exports as commands, createUnifiedMcpServer, server_exports as server } from './chunk-
|
|
2
|
-
export { createChildLogger, e2e_exports as e2e, getConfig, getLocalQaTools, getLogger, getQaTools, local_exports as localQa, mcp_exports as mcp, e2e_exports as qa, src_exports as shared } from './chunk-
|
|
1
|
+
export { src_exports as commands, createUnifiedMcpServer, server_exports as server } from './chunk-KGNRBL52.js';
|
|
2
|
+
export { createChildLogger, e2e_exports as e2e, getConfig, getLocalQaTools, getLogger, getQaTools, local_exports as localQa, mcp_exports as mcp, e2e_exports as qa, src_exports as shared } from './chunk-HBCASFGN.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "muggle",
|
|
3
3
|
"description": "Real-browser E2E acceptance for your coding agent: test the flows you changed and open the PR with screenshots. Built for flows other tools can't reach: managed test identities with live inboxes (magic links, email OTP and 2FA, verification mail, password resets), an encrypted secret store for login and test credentials, and a test graph that runs prerequisite flows first. Plain-English tests become replayable scripts on localhost or preview URLs.",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.28.0-staging.250",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Muggle AI",
|
|
7
7
|
"email": "support@muggle-ai.com"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "muggle",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.28.0-staging.250",
|
|
4
4
|
"description": "Real-browser E2E acceptance for your coding agent: test the flows you changed and open the PR with screenshots. Built for flows other tools can't reach: managed test identities with live inboxes (magic links, email OTP and 2FA, verification mail, password resets), an encrypted secret store for login and test credentials, and a test graph that runs prerequisite flows first. Plain-English tests become replayable scripts on localhost or preview URLs.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Muggle AI",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "muggle",
|
|
3
3
|
"displayName": "Muggle AI",
|
|
4
4
|
"description": "Real-browser E2E acceptance for your coding agent: test the flows you changed and open the PR with screenshots. Built for flows other tools can't reach: managed test identities with live inboxes (magic links, email OTP and 2FA, verification mail, password resets), an encrypted secret store for login and test credentials, and a test graph that runs prerequisite flows first. Plain-English tests become replayable scripts on localhost or preview URLs.",
|
|
5
|
-
"version": "5.
|
|
5
|
+
"version": "5.28.0-staging.250",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Muggle AI",
|
|
8
8
|
"email": "support@muggle-ai.com"
|
package/dist/plugin/.mcp.json
CHANGED
|
@@ -48,4 +48,4 @@ Store the returned `runId`, tagged with its `mode` and record. If a run fails, l
|
|
|
48
48
|
|
|
49
49
|
**A refused run is not a failure.** A result headed `Run refused — checkout is not on the latest …` launched nothing and has no `runId`: the `cwd` checkout lacks its base's latest commit (`BEHIND_BASE`) or carries another branch's commits (`FOREIGN_BASE`). Sync it per [`../sync-branch-with-base.md`](../sync-branch-with-base.md), re-invoke [`muggle-test-prepare`](../../muggle-test-prepare/SKILL.md) so the dev server serves the synced code, and run the case again. Never route it through failure handling or count it as a result.
|
|
50
50
|
|
|
51
|
-
**A stack refusal is not a failure either.** One local service stack serves every session on the machine, and [`muggle-test-prepare`](../../muggle-test-prepare/SKILL.md) claims the ports this checkout needs before it starts anything. A result headed `Run refused — the target port is leased to another checkout
|
|
51
|
+
**A stack refusal is not a failure either.** One local service stack serves every session on the machine, and [`muggle-test-prepare`](../../muggle-test-prepare/SKILL.md) claims the ports this checkout needs before it starts anything. A result headed `Run refused — the target port is leased to another checkout`, `Run refused — the service stack changed since this checkout claimed it` or `Run refused — this checkout's stack lease ended and its services were stopped` launched nothing: re-invoke prepare, which re-claims the stack and restarts what moved, or reports it busy with this checkout's place in line. A run result carrying the `service stack changed during this run` warning describes code that may not be this checkout's — re-run it before counting it. Never kill a peer's service to get a port, and never count any of these as a result.
|
|
@@ -37,7 +37,7 @@ Before asking anything, gather every fact you can resolve without the user:
|
|
|
37
37
|
|
|
38
38
|
1. **Candidate repo(s).** Match keywords in the task description against configured repo names. If one repo is an obvious match, propose it as the default; if two or three are plausible, list them.
|
|
39
39
|
2. **Current branch and default branch** for each candidate repo. Run `git -C <repo> symbolic-ref refs/remotes/origin/HEAD --short` and `git -C <repo> branch --show-current`. If the current branch is the default, the pre-flight must collect a new branch name.
|
|
40
|
-
3. **Local environment readiness.** Invoke [`muggle-test-prepare`](../muggle-test-prepare/SKILL.md) — the owner of dev-server/backend detection and service start (idempotent; it probes via `dev-server-readiness.md` internally). Use what it reports as the dev-server/URL pre-flight signal.
|
|
40
|
+
3. **Local environment readiness.** Invoke [`muggle-test-prepare`](../muggle-test-prepare/SKILL.md) — the owner of dev-server/backend detection and service start (idempotent; it probes via `dev-server-readiness.md` internally). Use what it reports as the dev-server/URL pre-flight signal, then release its lease at once: `muggle stack release --cwd "<checkout>"`. The cycle's first run is far off — after the questionnaire and the coding — and E2E acceptance invokes prepare again right before it; a lease held meanwhile blocks every other session's E2E.
|
|
41
41
|
4. **Backend health.** Covered by the same `muggle-test-prepare` invocation above (it probes backend health); note up/down.
|
|
42
42
|
5. **Muggle Test MCP auth.** Call `muggle-remote-auth-status`. If expired, you will ask to re-auth in the questionnaire.
|
|
43
43
|
6. **Candidate Muggle Test projects.** Call `muggle-remote-project-list` and rank by semantic match against the task description and the repo's dev URL.
|
|
@@ -8,14 +8,10 @@
|
|
|
8
8
|
- Per-case `mode` — `replay` or `regen`, chosen during replay-vs-regen classification.
|
|
9
9
|
- `cwd` — the PR-branch worktree if one exists, else the repo root.
|
|
10
10
|
|
|
11
|
-
## Local environment readiness
|
|
12
|
-
|
|
13
|
-
Before anything else, invoke [`muggle-test-prepare`](../muggle-test-prepare/SKILL.md) from `cwd` — the readiness/service-start owner (idempotent; halt on what it surfaces). It syncs `cwd` onto the latest base before starting anything, and serves every other repo from its latest default branch. The URL gate below only *selects* the target; prepare is what guarantees the right code is listening and compiled. Confirm its readiness table shows `cwd` as the `Source` of this repo's services, at `cwd`'s current HEAD — a different source means the run would test someone else's code; halt.
|
|
14
|
-
|
|
15
|
-
Then read `~/.muggle-ai/e2e-instructions/<key>.md` when it exists — this stack's recorded startup order, manual steps, and local gotchas. Use it to interpret what you see: a gotcha listed there explains a symptom that would otherwise read as a failure. Absent is normal.
|
|
16
|
-
|
|
17
11
|
## Pre-flight question — Local URL (gated by `autoSelectLocalHost`)
|
|
18
12
|
|
|
13
|
+
Both pre-flight gates resolve before prepare runs: prepare's grant holds ports peers wait for, and its lease ends if the first run does not follow soon after the services come up.
|
|
14
|
+
|
|
19
15
|
Skill responsibilities (the rest is in `preference-gates/autoSelectLocalHost.md`):
|
|
20
16
|
- **Read the cache**: `Muggle Test Last Host: <url>` session-context line, or `muggle-local-last-host-get`. Pass as `{lastHost}` substitution.
|
|
21
17
|
- **Auto-detect a suggested URL**: `lsof -iTCP -sTCP:LISTEN -nP | grep -E ':(3000|3001|4200|5173|8080)'`. Pass as `{suggestedHost}`.
|
|
@@ -30,6 +26,12 @@ Gate `showElectronBrowser` (per `preference-gates/README.md`). Resolve once; app
|
|
|
30
26
|
- `never` → pass `showUi: false`.
|
|
31
27
|
- `ask` → run Picker 1 from `preference-gates/showElectronBrowser.md` via `AskUserQuestion`; map the answer back to one of the actions above.
|
|
32
28
|
|
|
29
|
+
## Local environment readiness
|
|
30
|
+
|
|
31
|
+
Once both pre-flight questions are answered, invoke [`muggle-test-prepare`](../muggle-test-prepare/SKILL.md) from `cwd` — the readiness/service-start owner (idempotent; halt on what it surfaces). It syncs `cwd` onto the latest base before starting anything, and serves every other repo from its latest default branch. The URL gate above only *selects* the target; prepare is what guarantees the right code is listening and compiled. Confirm its readiness table shows `cwd` as the `Source` of this repo's services, at `cwd`'s current HEAD — a different source means the run would test someone else's code; halt. From here to the first run, ask nothing.
|
|
32
|
+
|
|
33
|
+
Then read `~/.muggle-ai/e2e-instructions/<key>.md` when it exists — this stack's recorded startup order, manual steps, and local gotchas. Use it to interpret what you see: a gotcha listed there explains a symptom that would otherwise read as a failure. Absent is normal.
|
|
34
|
+
|
|
33
35
|
## Prerequisite chain
|
|
34
36
|
|
|
35
37
|
Before the first run, resolve each selected test case's prerequisite chain and make every ancestor ready, per [`../_shared/test-case-chain-readiness.md`](../_shared/test-case-chain-readiness.md). Ancestors are generated, never replayed, root-first; the session an ancestor leaves behind is what the target starts from, so ordering here is what makes `freshSession: false` mean anything below.
|
|
@@ -31,7 +31,7 @@ Three gates apply, each per the standard procedure in [`preference-gates/README.
|
|
|
31
31
|
|
|
32
32
|
## Local environment prerequisites
|
|
33
33
|
|
|
34
|
-
Before
|
|
34
|
+
Before the first run, once every question that run needs is answered (Step 3), invoke [`muggle-test-prepare`](../muggle-test-prepare/SKILL.md) from the checkout under test. Halt on what it surfaces — a checkout it cannot bring onto the latest base included.
|
|
35
35
|
|
|
36
36
|
Then read `~/.muggle-ai/e2e-instructions/<key>.md` when it exists — this stack's recorded startup order, manual steps, and local gotchas. A gotcha listed there explains a symptom that would otherwise read as a failure. Absent is normal.
|
|
37
37
|
|
|
@@ -112,7 +112,9 @@ Prompt for projects: "Pick the project to group this test into:"
|
|
|
112
112
|
|
|
113
113
|
### 3. Ensure Local Services Are Ready
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
**Ask first.** Prepare's grant holds ports other sessions wait for, and its lease ends four minutes after the services come up with no run. So before invoking it, resolve every question asked on the way to the first run: Step 4's URL gate, Step 5's script choice (applied after Step 4a), and Step 7's `showElectronBrowser` gate. From prepare to the first execute call, ask nothing.
|
|
116
|
+
|
|
117
|
+
Then invoke the `muggle:muggle-test-prepare` skill via the `Skill` tool from the checkout under test — every run, even when services look up. Live PIDs prove nothing about which code they serve: prepare syncs this checkout onto the latest base, starts sibling services from base checkouts on the latest default branch, and keeps a running service only when it still serves the right commit. It fast-exits when everything is proven. Once it reports ready, check its readiness table: this checkout's services must show this checkout as their `Source` at its current HEAD. Then proceed to Step 4.
|
|
116
118
|
|
|
117
119
|
This step is especially important when the user's app depends on sibling services (a backend API, an auth service, etc.) that may not be running yet. The prepare skill handles discovery, startup, and cleanup so this skill doesn't have to.
|
|
118
120
|
|
|
@@ -91,11 +91,11 @@ Decide stages, in order; read each detail file when you reach it:
|
|
|
91
91
|
| [derive-service-graph](./steps/derive-service-graph.md) | Derive services from workspace manifests |
|
|
92
92
|
| [rebase-check](./steps/rebase-check.md) | Sync every checkout under test onto the latest base — replays too |
|
|
93
93
|
| [resolve-sources](./steps/resolve-sources.md) | Pick the checkout each service runs from — replays too |
|
|
94
|
-
| [claim-stack](./steps/claim-stack.md) | Claim the shared ports this run needs — replays too; busy returns to the caller |
|
|
95
94
|
| [scope](./steps/scope.md) | Frontend / backend / full stack |
|
|
96
95
|
| [viability-check](./steps/viability-check.md) | Exclude services that can't run locally |
|
|
97
96
|
| [identify-services](./steps/identify-services.md) | Confirm the derived graph; fill the gaps |
|
|
98
97
|
| [e2e-instructions](./steps/e2e-instructions.md) | Startup order, manual steps, local gotchas |
|
|
98
|
+
| [claim-stack](./steps/claim-stack.md) | Claim the shared ports this run needs, after the last question — replays too; busy returns to the caller |
|
|
99
99
|
|
|
100
100
|
The Decide phase's output is the **resolved prepare plan**: `services[]` (name, run directory from [resolve-sources](./steps/resolve-sources.md), start command, expected port, `external` flag, approval granted, and the port's `keep` / `start` / `restart` from [claim-stack](./steps/claim-stack.md)), `testingScope`, `excludedServices[]`, the recorded dev-server URL, the E2E run instructions, and resolved gate outcomes.
|
|
101
101
|
|
|
@@ -132,9 +132,11 @@ Stopped 3 services:
|
|
|
132
132
|
|
|
133
133
|
## Integration Contract (for other skills)
|
|
134
134
|
|
|
135
|
-
`muggle-test-feature-local`, `muggle-do`, and local-mode `muggle-test` MUST invoke this skill before
|
|
135
|
+
`muggle-test-feature-local`, `muggle-do`, and local-mode `muggle-test` MUST invoke this skill before their first run. Idempotent — fast exit when healthy. Never bypass on "the user knows their stack is up" — that assumption is why this skill exists.
|
|
136
136
|
|
|
137
|
-
|
|
137
|
+
**Ask first, then prepare.** Every question a caller asks before its first run — target, local URL, which script, browser visibility — is answered before it invokes this skill. From the grant to the first run nothing waits on the user or on any other work: a lease whose services are up ends four minutes later with no run, and its services stop ([claim-stack](./steps/claim-stack.md#the-lease)).
|
|
138
|
+
|
|
139
|
+
After a test run, the caller re-invokes for cleanup once the batch is done. Services stay up only while runs keep coming: two minutes after the last run, the lease ends and its services stop.
|
|
138
140
|
|
|
139
141
|
**The caller's checkout.** Invoke from the checkout under test — the worktree the change lives in. Its repo is the repo under test, and its services start from that checkout; every other repo's services start from that repo's base checkout ([resolve-sources](./steps/resolve-sources.md)). Pass `base` when the change targets a branch other than the remote default.
|
|
140
142
|
|
|
@@ -158,7 +160,8 @@ The readiness table names the directory and commit each service runs from, so th
|
|
|
158
160
|
- **Never leave orphan processes untracked** — every background PID goes into its checkout's tracking file.
|
|
159
161
|
- **Never kill a process the user started independently** — `external: true` survives cleanup.
|
|
160
162
|
- **Never kill or test against another checkout's service** — a listener tracked under a different checkout belongs to a peer session; see [check-running](./steps/check-running.md). The one exception is a port the stack claim planned as `restart`: no session's lease needs that service any more.
|
|
161
|
-
- **Never start, restart or kill a service on a port this run has not claimed** — [claim-stack](./steps/claim-stack.md) runs
|
|
163
|
+
- **Never start, restart or kill a service on a port this run has not claimed** — [claim-stack](./steps/claim-stack.md) runs before anything is started, and a busy stack goes back to the caller instead of being worked around.
|
|
164
|
+
- **Never hold a lease while waiting** — every question goes before [claim-stack](./steps/claim-stack.md); a `needs-input:` from the runner is a question too, so release before asking it and claim again after.
|
|
162
165
|
- **Never start code that is not on the latest base** — no replay, preference, or approval skips [rebase-check](./steps/rebase-check.md) or [resolve-sources](./steps/resolve-sources.md).
|
|
163
166
|
- **Never assume start commands** — verify via indicator file; confirm with user.
|
|
164
167
|
- **Bail early on non-viable services** — don't start what can't run locally.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Claim the stack
|
|
2
2
|
|
|
3
|
-
The machine runs **one** set of local services: one process per port, shared by every session on it. Before anything is checked, started or restarted, claim the ports this run needs, so two sessions never restart each other's services mid-run, or test against code that isn't theirs. Runs on every invocation, learning runs and replays alike,
|
|
3
|
+
The machine runs **one** set of local services: one process per port, shared by every session on it. Before anything is checked, started or restarted, claim the ports this run needs, so two sessions never restart each other's services mid-run, or test against code that isn't theirs. Runs on every invocation, learning runs and replays alike, as the **last** Decide stage — after every question this run asks is answered, right before dispatching the runner — in-session, never in the agent, because a busy stack has to reach the caller. A claim made before a question holds peers' ports while the user reads it.
|
|
4
4
|
|
|
5
5
|
## Claim
|
|
6
6
|
|
|
@@ -25,23 +25,29 @@ The claim answers at once. It never waits on a peer unless asked to.
|
|
|
25
25
|
|
|
26
26
|
## The lease
|
|
27
27
|
|
|
28
|
-
A granted claim is a lease, and it is held only while this session is testing. Each local execute run records itself on the lease while it is in flight. Nothing else extends it: not a re-claim, and not `muggle stack check`. So go straight from the grant to starting services and running. Never hold a lease while rebasing, editing code or waiting on CI; a renew loop cannot keep it. The lease ends, and its ports pass to whoever is next in line, when:
|
|
28
|
+
A granted claim is a lease, and it is held only while this session is testing. Each local execute run records itself on the lease while it is in flight. Nothing else extends it: not a re-claim, and not `muggle stack check`. So go straight from the grant to starting services and running. Never hold a lease while asking the user anything, rebasing, editing code or waiting on CI; a renew loop cannot keep it. The lease ends, and its ports pass to whoever is next in line, when:
|
|
29
29
|
|
|
30
30
|
- **Two minutes pass with no run in flight** after the last run ended. That covers the turn between consecutive runs of one batch. Debugging a failure, rebasing or fixing code takes longer: the lease ends, and prepare re-claims before the next run.
|
|
31
|
-
- **
|
|
31
|
+
- **Eight minutes pass after the grant before every claimed port serves the claimed code.** That is the time prepare has to install and start its services. A port counts as served once a service tracked at the claimed directory and commit is alive on it.
|
|
32
|
+
- **Four minutes pass after the services come up with no run started.** That covers the dev server's compile, the smoke test and the first execute call — not a question to the user.
|
|
32
33
|
- **The session died.** Each run records the session's MCP server as a holder. Once every holder has exited and a minute has passed, the lease ends. The minute rides out an MCP server restart.
|
|
33
34
|
- **It is released** in [Cleanup](../SKILL.md#cleanup).
|
|
34
35
|
|
|
36
|
+
A lease that ends any way but a release is remembered for 30 minutes: the checkout's next run is refused `Run refused — this checkout's stack lease ended and its services were stopped` (`muggle stack check` answers `ENDED`). Re-invoke prepare; it claims again.
|
|
37
|
+
|
|
35
38
|
**A lease that ends takes its services with it.** Whatever listens on a port whose code no remaining lease needs is stopped. That covers services prepare started and ones started by hand, along with their launchers. The session's own MCP server ends the lease two minutes after its last run. Otherwise the next claim, run or release on the machine does it. A service marked `external: true` is never stopped, and neither is one another lease still needs.
|
|
36
39
|
|
|
37
40
|
The lease belongs to the checkout, not to one run. Every test case of the same change, including cases run in parallel, runs under it, and none of them waits on the others.
|
|
38
41
|
|
|
39
|
-
The line is
|
|
42
|
+
The line is ordered per port by the checkout's open pull request: the oldest-opened PR first, so the work nearest to merging drains the backlog, and a re-run after a rebase or a review fix goes back ahead of newer PRs rather than to the end. Checkouts with no open PR yet follow, in the order they first asked. The claim finds the PR itself with `gh pr view`; without `gh` or a PR, the checkout ranks as one with none. `muggle stack status` shows each waiter's PR. The machine-wide execution lock every local run takes orders its waiters the same way.
|
|
43
|
+
|
|
44
|
+
The line is pulled, not pushed: nothing calls a waiting session back. A session keeps its place by asking again within the suggested delay, and is served before anyone behind it who wants the same port. A session that stops asking keeps its turn for 30 seconds after the port frees, and its place for 10 minutes.
|
|
40
45
|
|
|
41
46
|
Holding a lease is optimistic, not exclusive. Nothing stops another process from binding a port. Instead, the local execute tools check the lease before every run and again when it ends:
|
|
42
47
|
|
|
43
48
|
- A run whose port another session's lease holds, from a checkout that never claimed it, is refused: `Run refused — the target port is leased to another checkout`.
|
|
44
49
|
- A run whose claimed port no longer serves the claimed code is refused: `Run refused — the service stack changed since this checkout claimed it`. Re-invoke prepare: it re-claims and restarts what moved.
|
|
50
|
+
- A run whose lease ended unreleased is refused: `Run refused — this checkout's stack lease ended and its services were stopped`. Re-invoke prepare.
|
|
45
51
|
- A run during which the stack changed returns its result with a warning, and that result is not trusted until it has been re-run.
|
|
46
52
|
|
|
47
53
|
None of these is a test result. Never route them through failure handling.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"release": "5.26.0",
|
|
3
|
-
"buildId": "run-
|
|
4
|
-
"commitSha": "
|
|
5
|
-
"buildTime": "2026-10-
|
|
3
|
+
"buildId": "run-250-1",
|
|
4
|
+
"commitSha": "ba9d56238c918b107aabc3037ceab011d08e3e85",
|
|
5
|
+
"buildTime": "2026-10-29T23:38:01Z",
|
|
6
6
|
"serviceName": "muggle-ai-works-mcp"
|
|
7
7
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ACTION_SCRIPT_FILE_NAME, BASE_REMOTE_NAME, BLOCKING_FRESHNESS_VERDICTS, BRANCH_CREATION_REFLOG_PREFIX, BUSY_EXIT_CODE, CLAUDE_CODE_AUTH_STATUS_ARGS, CLAUDE_CODE_CHECK_TIMEOUT_MS, CLAUDE_CODE_EXECUTABLE, CLAUDE_CODE_EXECUTABLE_NOT_FOUND_CODE, CLAUDE_CODE_SESSION_LINK_ENV_VARS, CLAUDE_CODE_VERSION_ARGS, CheckoutFreshnessVerdict, ClaudeCodeReadiness, DEFAULT_LOCAL_LLM_BASE_URL, DEFAULT_PREFERENCES, ELECTRON_APP_EXECUTABLE_PATH_SEGMENTS, ElectronAppPlatform, ElectronAppReleaseStream, HTML_VIEW_OFFER_LINE, LLM_CREDENTIALS_FILE_MODE, LLM_CREDENTIALS_FILE_NAME, LLM_SOURCE_HEADER, LLM_SOURCE_OPTIONS, LLM_SOURCE_OWNED_ENV_NAMES, LLM_SOURCE_PROMPT, LLM_SOURCE_SCOPE_NOTE, LlmSource, MASKED_API_KEY_PREFIX, MASKED_API_KEY_VISIBLE_CHARS, MAX_LISTED_INHERITED_COMMITS, ONBOARDING_BLANKET_OPTIONS, ONBOARDING_CHOICE_GROUPS, ONBOARDING_MAX_OFFERS, ONBOARDING_PRIMER_BULLETS, ONBOARDING_PRIMER_HEADLINE, ONBOARDING_TOGGLE_GROUPS, ONBOARDING_TOGGLE_PROMPT, ONBOARDING_UNSELECTED_TOGGLE_VALUE, OnboardingBlanketChoice, OnboardingGroupKind, PREFERENCES_FILE_NAME, PREFERENCES_PROJECT_DIR_NAME, PREFERENCES_SCHEMA, PREFERENCES_VERSION, PREFERENCE_ALLOWED_VALUES, PreferenceKey, PreferenceValue, ProjectPreferencesReconcileOutcome, RUN_SESSIONS_DIR_NAME, RUN_VIEW_DASHBOARD_LINK_LABEL, RunViewError, RuntimeTarget, SCREENSHOT_DIR_RELATIVE_PATH, SCREENSHOT_FILE_EXTENSION, StackCheckVerdict, StackClaimVerdict, StackLeaseEndReason, StackMismatchKind, StackPortAction, WATCHER_LIFETIME_SECONDS, WATCHER_LIFETIME_UNBOUNDED_SECONDS, applyLlmSourceSelection, applyOnboardingAnswers, assertDeviceCodeClientProvisioned, buildElectronAppChecksumsUrl, buildElectronAppDirName, buildElectronAppReleaseAssetUrl, buildElectronAppReleaseTag, buildLlmSourcePlan, buildOnboardingPlan, buildToolInputJsonSchema, calculateFileChecksum, checkServiceStack, claimServiceStack, compareQueuePlaces, completeOnboarding, createApiKeyWithToken, createChildLogger, decideStackClaim, decideStackReap, decideStackRelease, decideStackRunEnd, decideStackRunStart, deleteApiKeyData, deleteCredentials, describeLlmSourceStatus, describeStackMismatch, e2e_exports as e2e, evaluateCheckoutFreshness, findVacatedNeeds, finishStackRun, formatPreferencesOneLiner, getActiveElectronAppReleaseStream, getActiveRuntimeTarget, getApiKey, getApiKeyFilePath, getAuthService, getBundledElectronAppVersion, getCallerCredentials, getCallerCredentialsAsync, getChecksumForPlatform, getConfig, getCredentialsFilePath, getDashboardBaseUrl, getDataDir2 as getDataDir, getDownloadBaseUrl, getElectronAppChecksums, getElectronAppDir, getElectronAppReleaseTagPrefix, getElectronAppSignedFromVersion, getElectronAppVersion, getElectronAppVersionSource, getLlmSourceOption, getLocalQaTools, getLogger, getOnboardingToggleKeys, getPlatformKey, getQaTools, getReleaseSignerIdentityUri, getRunSessionsDir, getValidApiKeyData, getValidCredentials, hasApiKey, isElectronAppInstalled, isElectronAppPlatform, isFirstRun, isValidLocalBaseUrl, loadApiKeyData, loadCredentials, local_exports as localQa, lookUpCheckoutPullRequest, markOnboardingCompleted, maskLlmApiKey, mcp_exports as mcp, needsConflict, needsOnboarding, openBrowserUrl, parseElectronAppDirName, performLogin, performLogout, pollDeviceCode, portOfUrl, e2e_exports as qa, readPreferencesMetadata, readRunView, readServiceStackStatus, reapServiceStack, reconcileProjectPreferences, recordOnboardingSkip, releaseServiceStack, renderRunViewHtml, renderRunViewText, resetConfig, resetLogger, resetPreference, resolveActiveProfile, resolveActiveReleaseStream, resolveActiveReleaseTagPrefix, resolveBlanketPreferences, resolveElectronAppExecutablePath, resolveElectronAppPathOrNull, resolveLlmSourceStatus, resolveOnboardingAnswers, resolvePreferences, resolveRunSessionPath, resolveRuntimeTarget, resolveTogglePreferences, runGitCommand, saveApiKey, saveApiKeyData, saveCredentials, startDeviceCodeFlow, startStackRun, stopVacatedServices, toDisplayRef, toolRequiresAuth, validatePreference, verifyFileChecksum, writeLlmEnvBlock, writePreferences, writePreferencesWithMetadata, writeRunViewHtml } from './chunk-HBCASFGN.js';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@muggleai/works",
|
|
3
3
|
"mcpName": "io.github.multiplex-ai/muggle",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.28.0-staging.250",
|
|
5
5
|
"description": "13 years of Microsoft hyperscale engineering, packaged as a harness for your AI coding agent.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/index.js",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"muggleConfig": {
|
|
64
64
|
"electronAppVersion": "1.17.0",
|
|
65
65
|
"downloadBaseUrl": "https://github.com/multiplex-ai/muggle-ai-works/releases/download",
|
|
66
|
-
"runtimeTargetDefault": "
|
|
66
|
+
"runtimeTargetDefault": "staging",
|
|
67
67
|
"checksumsByStream": {
|
|
68
68
|
"production": {
|
|
69
69
|
"win32-x64": "03687e6c511f407875354954cb1b0f8e95d4ad0602174d76388a52d1aa1554e4",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "muggle",
|
|
3
3
|
"description": "Real-browser E2E acceptance for your coding agent: test the flows you changed and open the PR with screenshots. Built for flows other tools can't reach: managed test identities with live inboxes (magic links, email OTP and 2FA, verification mail, password resets), an encrypted secret store for login and test credentials, and a test graph that runs prerequisite flows first. Plain-English tests become replayable scripts on localhost or preview URLs.",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.28.0-staging.250",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Muggle AI",
|
|
7
7
|
"email": "support@muggle-ai.com"
|
package/plugin/.codex-mcp.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "muggle",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.28.0-staging.250",
|
|
4
4
|
"description": "Real-browser E2E acceptance for your coding agent: test the flows you changed and open the PR with screenshots. Built for flows other tools can't reach: managed test identities with live inboxes (magic links, email OTP and 2FA, verification mail, password resets), an encrypted secret store for login and test credentials, and a test graph that runs prerequisite flows first. Plain-English tests become replayable scripts on localhost or preview URLs.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Muggle AI",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "muggle",
|
|
3
3
|
"displayName": "Muggle AI",
|
|
4
4
|
"description": "Real-browser E2E acceptance for your coding agent: test the flows you changed and open the PR with screenshots. Built for flows other tools can't reach: managed test identities with live inboxes (magic links, email OTP and 2FA, verification mail, password resets), an encrypted secret store for login and test credentials, and a test graph that runs prerequisite flows first. Plain-English tests become replayable scripts on localhost or preview URLs.",
|
|
5
|
-
"version": "5.
|
|
5
|
+
"version": "5.28.0-staging.250",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Muggle AI",
|
|
8
8
|
"email": "support@muggle-ai.com"
|
package/plugin/.mcp.json
CHANGED
|
@@ -48,4 +48,4 @@ Store the returned `runId`, tagged with its `mode` and record. If a run fails, l
|
|
|
48
48
|
|
|
49
49
|
**A refused run is not a failure.** A result headed `Run refused — checkout is not on the latest …` launched nothing and has no `runId`: the `cwd` checkout lacks its base's latest commit (`BEHIND_BASE`) or carries another branch's commits (`FOREIGN_BASE`). Sync it per [`../sync-branch-with-base.md`](../sync-branch-with-base.md), re-invoke [`muggle-test-prepare`](../../muggle-test-prepare/SKILL.md) so the dev server serves the synced code, and run the case again. Never route it through failure handling or count it as a result.
|
|
50
50
|
|
|
51
|
-
**A stack refusal is not a failure either.** One local service stack serves every session on the machine, and [`muggle-test-prepare`](../../muggle-test-prepare/SKILL.md) claims the ports this checkout needs before it starts anything. A result headed `Run refused — the target port is leased to another checkout
|
|
51
|
+
**A stack refusal is not a failure either.** One local service stack serves every session on the machine, and [`muggle-test-prepare`](../../muggle-test-prepare/SKILL.md) claims the ports this checkout needs before it starts anything. A result headed `Run refused — the target port is leased to another checkout`, `Run refused — the service stack changed since this checkout claimed it` or `Run refused — this checkout's stack lease ended and its services were stopped` launched nothing: re-invoke prepare, which re-claims the stack and restarts what moved, or reports it busy with this checkout's place in line. A run result carrying the `service stack changed during this run` warning describes code that may not be this checkout's — re-run it before counting it. Never kill a peer's service to get a port, and never count any of these as a result.
|
|
@@ -37,7 +37,7 @@ Before asking anything, gather every fact you can resolve without the user:
|
|
|
37
37
|
|
|
38
38
|
1. **Candidate repo(s).** Match keywords in the task description against configured repo names. If one repo is an obvious match, propose it as the default; if two or three are plausible, list them.
|
|
39
39
|
2. **Current branch and default branch** for each candidate repo. Run `git -C <repo> symbolic-ref refs/remotes/origin/HEAD --short` and `git -C <repo> branch --show-current`. If the current branch is the default, the pre-flight must collect a new branch name.
|
|
40
|
-
3. **Local environment readiness.** Invoke [`muggle-test-prepare`](../muggle-test-prepare/SKILL.md) — the owner of dev-server/backend detection and service start (idempotent; it probes via `dev-server-readiness.md` internally). Use what it reports as the dev-server/URL pre-flight signal.
|
|
40
|
+
3. **Local environment readiness.** Invoke [`muggle-test-prepare`](../muggle-test-prepare/SKILL.md) — the owner of dev-server/backend detection and service start (idempotent; it probes via `dev-server-readiness.md` internally). Use what it reports as the dev-server/URL pre-flight signal, then release its lease at once: `muggle stack release --cwd "<checkout>"`. The cycle's first run is far off — after the questionnaire and the coding — and E2E acceptance invokes prepare again right before it; a lease held meanwhile blocks every other session's E2E.
|
|
41
41
|
4. **Backend health.** Covered by the same `muggle-test-prepare` invocation above (it probes backend health); note up/down.
|
|
42
42
|
5. **Muggle Test MCP auth.** Call `muggle-remote-auth-status`. If expired, you will ask to re-auth in the questionnaire.
|
|
43
43
|
6. **Candidate Muggle Test projects.** Call `muggle-remote-project-list` and rank by semantic match against the task description and the repo's dev URL.
|