@muggleai/works 5.31.0-staging.277 → 5.31.0-staging.278
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-FVG7CYB7.js → chunk-CGKARU5D.js} +97 -10
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- 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/release-manifest.json +3 -3
- package/package.json +1 -1
- 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
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"name": "muggleai",
|
|
10
10
|
"source": "./plugin",
|
|
11
11
|
"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.",
|
|
12
|
-
"version": "5.31.0-staging.
|
|
12
|
+
"version": "5.31.0-staging.278"
|
|
13
13
|
}
|
|
14
14
|
]
|
|
15
15
|
}
|
|
@@ -2937,6 +2937,11 @@ var MUGGLE_MCP_SERVER_PATTERN = /@muggleai[\\/]works\b.*\bserve\b/;
|
|
|
2937
2937
|
var PR_WATCH_LOOP_PATTERN = /pr-watch-loop\.sh\b.*?--slot\s+(?:"([^"]+)"|(\S+))/;
|
|
2938
2938
|
var CLAUDE_SESSION_REGISTRY_DIR_NAME = "sessions";
|
|
2939
2939
|
var CLAUDE_PROJECTS_DIR_NAME = "projects";
|
|
2940
|
+
var CLAUDE_JOBS_DIR_NAME = "jobs";
|
|
2941
|
+
var CLAUDE_JOB_STATE_FILE_NAME = "state.json";
|
|
2942
|
+
var SESSION_NAME_FLAG = "--name";
|
|
2943
|
+
var CLAUDE_PROMPT_NAME_MAX_LENGTH = 200;
|
|
2944
|
+
var CUSTOM_TITLE_ENTRY_TYPE = "custom-title";
|
|
2940
2945
|
var CLAUDE_INSTALLED_PLUGINS_RELATIVE_PATH = ["plugins", "installed_plugins.json"];
|
|
2941
2946
|
var CLAUDE_GLOBAL_CONFIG_FILE_NAME = ".claude.json";
|
|
2942
2947
|
var CLAUDE_SETTINGS_FILE_NAME = "settings.json";
|
|
@@ -3004,6 +3009,7 @@ function resolveWatchPaths(env = process.env) {
|
|
|
3004
3009
|
slotsDir: join(muggleDataDir, ...PR_WATCH_SLOTS_RELATIVE_PATH),
|
|
3005
3010
|
claudeSessionRegistryDir: join(claudeConfigDir, CLAUDE_SESSION_REGISTRY_DIR_NAME),
|
|
3006
3011
|
claudeProjectsDir: join(claudeConfigDir, CLAUDE_PROJECTS_DIR_NAME),
|
|
3012
|
+
claudeJobsDir: join(claudeConfigDir, CLAUDE_JOBS_DIR_NAME),
|
|
3007
3013
|
claudeInstalledPluginsFile: join(claudeConfigDir, ...CLAUDE_INSTALLED_PLUGINS_RELATIVE_PATH),
|
|
3008
3014
|
claudeGlobalConfigFile: join(env.CLAUDE_CONFIG_DIR || homedir(), CLAUDE_GLOBAL_CONFIG_FILE_NAME),
|
|
3009
3015
|
claudeSettingsFile: join(claudeConfigDir, CLAUDE_SETTINGS_FILE_NAME),
|
|
@@ -5167,6 +5173,44 @@ function readCodexSessions(sessionsDir, nowMs) {
|
|
|
5167
5173
|
}
|
|
5168
5174
|
return sessions;
|
|
5169
5175
|
}
|
|
5176
|
+
function jobStatePath(jobsDir, shortId) {
|
|
5177
|
+
return join(jobsDir, shortId, CLAUDE_JOB_STATE_FILE_NAME);
|
|
5178
|
+
}
|
|
5179
|
+
function readJobState(statePath) {
|
|
5180
|
+
try {
|
|
5181
|
+
const parsed = JSON.parse(readFileSync(statePath, "utf-8"));
|
|
5182
|
+
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : void 0;
|
|
5183
|
+
} catch {
|
|
5184
|
+
return void 0;
|
|
5185
|
+
}
|
|
5186
|
+
}
|
|
5187
|
+
function respawnFlagsOf(state) {
|
|
5188
|
+
return Array.isArray(state.respawnFlags) ? state.respawnFlags.filter((flag) => typeof flag === "string") : [];
|
|
5189
|
+
}
|
|
5190
|
+
function nameFlagOf(respawnFlags) {
|
|
5191
|
+
const flagIndex = respawnFlags.lastIndexOf(SESSION_NAME_FLAG);
|
|
5192
|
+
return flagIndex === -1 ? void 0 : optionalString(respawnFlags[flagIndex + 1]);
|
|
5193
|
+
}
|
|
5194
|
+
function readClaudeJob(jobsDir, shortId) {
|
|
5195
|
+
const state = readJobState(jobStatePath(jobsDir, shortId));
|
|
5196
|
+
if (state === void 0) return void 0;
|
|
5197
|
+
const intent = optionalString(state.intent);
|
|
5198
|
+
return { respawnFlags: respawnFlagsOf(state), ...intent === void 0 ? {} : { intent } };
|
|
5199
|
+
}
|
|
5200
|
+
function pinClaudeJobName(jobsDir, shortId, name) {
|
|
5201
|
+
const statePath = jobStatePath(jobsDir, shortId);
|
|
5202
|
+
const state = readJobState(statePath);
|
|
5203
|
+
if (state === void 0) return false;
|
|
5204
|
+
const respawnFlags = respawnFlagsOf(state);
|
|
5205
|
+
if (nameFlagOf(respawnFlags) === name) return true;
|
|
5206
|
+
const otherFlags = respawnFlags.filter((flag, index) => flag !== SESSION_NAME_FLAG && respawnFlags[index - 1] !== SESSION_NAME_FLAG);
|
|
5207
|
+
try {
|
|
5208
|
+
writeFileSync(statePath, JSON.stringify({ ...state, respawnFlags: [...otherFlags, SESSION_NAME_FLAG, name] }, null, 2));
|
|
5209
|
+
return true;
|
|
5210
|
+
} catch {
|
|
5211
|
+
return false;
|
|
5212
|
+
}
|
|
5213
|
+
}
|
|
5170
5214
|
function findTranscriptPath(projectsDir, sessionId) {
|
|
5171
5215
|
let projectDirNames;
|
|
5172
5216
|
try {
|
|
@@ -5211,6 +5255,23 @@ function readLimitStop(projectsDir, sessionId) {
|
|
|
5211
5255
|
}
|
|
5212
5256
|
return void 0;
|
|
5213
5257
|
}
|
|
5258
|
+
function readCustomTitle(projectsDir, sessionId) {
|
|
5259
|
+
const transcriptPath = findTranscriptPath(projectsDir, sessionId);
|
|
5260
|
+
if (transcriptPath === void 0) return void 0;
|
|
5261
|
+
let transcript;
|
|
5262
|
+
try {
|
|
5263
|
+
transcript = readFileSync(transcriptPath, "utf-8");
|
|
5264
|
+
} catch {
|
|
5265
|
+
return void 0;
|
|
5266
|
+
}
|
|
5267
|
+
let customTitle;
|
|
5268
|
+
for (const line of transcript.split("\n")) {
|
|
5269
|
+
if (!line.includes(CUSTOM_TITLE_ENTRY_TYPE)) continue;
|
|
5270
|
+
const entry = parseTranscriptLine(line);
|
|
5271
|
+
if (entry?.type === CUSTOM_TITLE_ENTRY_TYPE) customTitle = optionalString(entry.customTitle) ?? customTitle;
|
|
5272
|
+
}
|
|
5273
|
+
return customTitle;
|
|
5274
|
+
}
|
|
5214
5275
|
function findClaudeSessionCwd(projectsDir, sessionId) {
|
|
5215
5276
|
const transcriptPath = findTranscriptPath(projectsDir, sessionId);
|
|
5216
5277
|
if (transcriptPath === void 0) return void 0;
|
|
@@ -5430,7 +5491,7 @@ function startServiceLoops(assessments, nowMs, isPollingDisabled, ownerSessionOf
|
|
|
5430
5491
|
}
|
|
5431
5492
|
|
|
5432
5493
|
// src/watch/session-restart.ts
|
|
5433
|
-
function resumeSession(target, runCommand, waitForExit, isSessionRunning2) {
|
|
5494
|
+
function resumeSession(target, runCommand, waitForExit, isSessionRunning2, pinSessionName) {
|
|
5434
5495
|
const runningSessionId = target.runningSessionId ?? target.sessionId;
|
|
5435
5496
|
if (target.runningShortId !== void 0) {
|
|
5436
5497
|
runCommand("claude", ["stop", target.runningShortId]);
|
|
@@ -5438,6 +5499,7 @@ function resumeSession(target, runCommand, waitForExit, isSessionRunning2) {
|
|
|
5438
5499
|
} else if (isSessionRunning2(runningSessionId)) {
|
|
5439
5500
|
return { outcome: "still-running" /* StillRunning */ };
|
|
5440
5501
|
}
|
|
5502
|
+
pinSessionName(target.sessionId, target.runningShortId ?? shortSessionId(target.sessionId));
|
|
5441
5503
|
const resumed = runCommand("claude", ["--resume", target.sessionId, "--bg", target.prompt], { cwd: target.cwd, isStderrCaptured: true });
|
|
5442
5504
|
if (resumed?.exitCode !== 0) return { outcome: "failed" /* Failed */ };
|
|
5443
5505
|
const backgroundedShortId = resumed.stdout.match(BACKGROUNDED_SESSION_ID_PATTERN)?.[1];
|
|
@@ -5530,7 +5592,8 @@ function executeWatcherRecoveries(planned, attemptsByOwner, deps) {
|
|
|
5530
5592
|
{ sessionId: recovery.ownerSessionId, cwd, prompt, runningShortId: recovery.ownerShortId, runningSessionId: recovery.ownerRunningSessionId },
|
|
5531
5593
|
deps.runCommand,
|
|
5532
5594
|
deps.waitForExit,
|
|
5533
|
-
deps.isSessionRunning
|
|
5595
|
+
deps.isSessionRunning,
|
|
5596
|
+
deps.pinSessionName
|
|
5534
5597
|
);
|
|
5535
5598
|
if (resumed.outcome === "continued" /* Continued */) {
|
|
5536
5599
|
markServiceLoopReportDelivered(recovery.slotDir);
|
|
@@ -5889,7 +5952,8 @@ function executeLimitResumes(planned, attemptsBySession, deps) {
|
|
|
5889
5952
|
{ sessionId: decision.sessionId, cwd, prompt: LIMIT_RESUME_PROMPT, runningShortId: decision.runningShortId },
|
|
5890
5953
|
deps.runCommand,
|
|
5891
5954
|
deps.waitForExit,
|
|
5892
|
-
deps.isSessionRunning
|
|
5955
|
+
deps.isSessionRunning,
|
|
5956
|
+
deps.pinSessionName
|
|
5893
5957
|
);
|
|
5894
5958
|
outcome = continuedOutcomeOf(resumed.outcome);
|
|
5895
5959
|
message = continueMessageOf(resumed, decision.reason);
|
|
@@ -6159,7 +6223,7 @@ function planReloadFanOut(sessions, pending, watchOwnerSessionIds, maxRestarts =
|
|
|
6159
6223
|
}
|
|
6160
6224
|
return decisions;
|
|
6161
6225
|
}
|
|
6162
|
-
function executeReloadFanOut(decisions, runCommand, canRestartIn) {
|
|
6226
|
+
function executeReloadFanOut(decisions, runCommand, canRestartIn, pinSessionName) {
|
|
6163
6227
|
const restartableByCwd = /* @__PURE__ */ new Map();
|
|
6164
6228
|
const isRestartable = (cwd) => {
|
|
6165
6229
|
if (!restartableByCwd.has(cwd)) restartableByCwd.set(cwd, canRestartIn(cwd));
|
|
@@ -6172,6 +6236,7 @@ function executeReloadFanOut(decisions, runCommand, canRestartIn) {
|
|
|
6172
6236
|
report.untrustedSessionIds.push(decision.sessionId);
|
|
6173
6237
|
continue;
|
|
6174
6238
|
}
|
|
6239
|
+
pinSessionName(decision.sessionId, decision.shortId);
|
|
6175
6240
|
const isRestarted = runCommand("claude", ["respawn", decision.shortId])?.exitCode === 0;
|
|
6176
6241
|
(isRestarted ? report.respawnedSessionIds : report.failedSessionIds).push(decision.sessionId);
|
|
6177
6242
|
}
|
|
@@ -6372,6 +6437,16 @@ function stampSessionLoads(sessions, table, tracked, installs, nowMs) {
|
|
|
6372
6437
|
});
|
|
6373
6438
|
}
|
|
6374
6439
|
|
|
6440
|
+
// src/watch/session-names.ts
|
|
6441
|
+
function isPlaceholderSessionName(name, shortId) {
|
|
6442
|
+
return name === void 0 || name === shortId || name === LIMIT_RESUME_PROMPT || name.startsWith("/");
|
|
6443
|
+
}
|
|
6444
|
+
function resolveSessionName(sources) {
|
|
6445
|
+
const promptName = sources.job?.intent?.replace(/\s+/g, " ").trim().slice(0, CLAUDE_PROMPT_NAME_MAX_LENGTH);
|
|
6446
|
+
const candidates = [sources.customTitle, sources.job && nameFlagOf(sources.job.respawnFlags), sources.registeredName, promptName];
|
|
6447
|
+
return candidates.find((candidate) => !isPlaceholderSessionName(candidate, sources.shortId));
|
|
6448
|
+
}
|
|
6449
|
+
|
|
6375
6450
|
// src/watch/terminal-pr-cache.ts
|
|
6376
6451
|
function isRecheckDue(record, nowMs) {
|
|
6377
6452
|
if (!record) return true;
|
|
@@ -6677,12 +6752,12 @@ function reloadRecordFor(state, targetAtMs) {
|
|
|
6677
6752
|
}
|
|
6678
6753
|
function runReloadPass(record, claudeSessions, watchOwnerSessionIds, deps, at) {
|
|
6679
6754
|
const decisions = planReloadFanOut(claudeSessions, record, watchOwnerSessionIds);
|
|
6680
|
-
const report = executeReloadFanOut(decisions, deps.runCommand, canRestartVia(deps));
|
|
6755
|
+
const report = executeReloadFanOut(decisions, deps.runCommand, canRestartVia(deps), pinSessionNameVia(deps, claudeSessions));
|
|
6681
6756
|
return { report, pendingReload: advancePendingReload(record, report), events: reloadEvents(report, at) };
|
|
6682
6757
|
}
|
|
6683
6758
|
function runRemoteControlPass(login, claudeSessions, restartsBySession, maxRestarts, deps, at) {
|
|
6684
6759
|
const plan = planRemoteControlRestarts(claudeSessions, login, restartsBySession, deps.nowMs, maxRestarts);
|
|
6685
|
-
const executed = executeReloadFanOut(plan.decisions, deps.runCommand, canRestartVia(deps));
|
|
6760
|
+
const executed = executeReloadFanOut(plan.decisions, deps.runCommand, canRestartVia(deps), pinSessionNameVia(deps, claudeSessions));
|
|
6686
6761
|
return {
|
|
6687
6762
|
report: { ...executed, connectedSessionIds: plan.connectedSessionIds },
|
|
6688
6763
|
restartsBySession: recordRemoteControlRestarts(restartsBySession, executed, deps.nowMs),
|
|
@@ -6698,14 +6773,26 @@ function waitForExitVia(deps) {
|
|
|
6698
6773
|
function isSessionRunningVia(deps) {
|
|
6699
6774
|
return (sessionId) => isSessionRunning(deps.runCommand, deps.paths.claudeSessionRegistryDir, sessionId) !== false;
|
|
6700
6775
|
}
|
|
6701
|
-
function
|
|
6776
|
+
function pinSessionNameVia(deps, claudeSessions) {
|
|
6777
|
+
return (sessionId, shortId) => {
|
|
6778
|
+
const name = resolveSessionName({
|
|
6779
|
+
shortId,
|
|
6780
|
+
customTitle: readCustomTitle(deps.paths.claudeProjectsDir, sessionId),
|
|
6781
|
+
job: readClaudeJob(deps.paths.claudeJobsDir, shortId),
|
|
6782
|
+
registeredName: claudeSessions?.find((session) => session.sessionId === sessionId)?.name
|
|
6783
|
+
});
|
|
6784
|
+
if (name !== void 0) pinClaudeJobName(deps.paths.claudeJobsDir, shortId, name);
|
|
6785
|
+
};
|
|
6786
|
+
}
|
|
6787
|
+
function resumeDepsOf(deps, claudeSessions) {
|
|
6702
6788
|
return {
|
|
6703
6789
|
runCommand: deps.runCommand,
|
|
6704
6790
|
claudeProjectsDir: deps.paths.claudeProjectsDir,
|
|
6705
6791
|
nowMs: deps.nowMs,
|
|
6706
6792
|
canRestartIn: canRestartVia(deps),
|
|
6707
6793
|
waitForExit: waitForExitVia(deps),
|
|
6708
|
-
isSessionRunning: isSessionRunningVia(deps)
|
|
6794
|
+
isSessionRunning: isSessionRunningVia(deps),
|
|
6795
|
+
pinSessionName: pinSessionNameVia(deps, claudeSessions)
|
|
6709
6796
|
};
|
|
6710
6797
|
}
|
|
6711
6798
|
function runLimitResumePass(claudeSessions, attemptsBySession, maxContinues, deps) {
|
|
@@ -6722,7 +6809,7 @@ function runLimitResumePass(claudeSessions, attemptsBySession, maxContinues, dep
|
|
|
6722
6809
|
isClaudeUsageLimitInForce(deps.paths.claudeGlobalConfigFile, deps.nowMs),
|
|
6723
6810
|
maxContinues
|
|
6724
6811
|
);
|
|
6725
|
-
const executed = executeLimitResumes(planned, attemptsBySession, resumeDepsOf(deps));
|
|
6812
|
+
const executed = executeLimitResumes(planned, attemptsBySession, resumeDepsOf(deps, claudeSessions));
|
|
6726
6813
|
return { report: { decisions: executed.decisions }, attemptsBySession: executed.attemptsBySession, events: executed.events };
|
|
6727
6814
|
}
|
|
6728
6815
|
function reloadTargetAtMs(deps, installs) {
|
|
@@ -6787,7 +6874,7 @@ function runTickUnlocked(deps) {
|
|
|
6787
6874
|
sessionsById,
|
|
6788
6875
|
isClaudeUsageLimitInForce(deps.paths.claudeGlobalConfigFile, deps.nowMs)
|
|
6789
6876
|
);
|
|
6790
|
-
const executed = executeWatcherRecoveries(planned, recoveryAttemptsByOwner, resumeDepsOf(deps));
|
|
6877
|
+
const executed = executeWatcherRecoveries(planned, recoveryAttemptsByOwner, resumeDepsOf(deps, claudeSessions));
|
|
6791
6878
|
recoveries = executed.recoveries;
|
|
6792
6879
|
recoveryAttemptsByOwner = executed.attemptsByOwner;
|
|
6793
6880
|
events.push(...executed.events);
|
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-
|
|
1
|
+
export { src_exports as commands, createUnifiedMcpServer, server_exports as server } from './chunk-CGKARU5D.js';
|
|
2
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-NTEBYUCA.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.31.0-staging.
|
|
4
|
+
"version": "5.31.0-staging.278",
|
|
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.31.0-staging.
|
|
3
|
+
"version": "5.31.0-staging.278",
|
|
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.31.0-staging.
|
|
5
|
+
"version": "5.31.0-staging.278",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Muggle AI",
|
|
8
8
|
"email": "support@muggle-ai.com"
|
package/dist/plugin/.mcp.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"release": "5.26.0",
|
|
3
|
-
"buildId": "run-
|
|
4
|
-
"commitSha": "
|
|
5
|
-
"buildTime": "2026-11-
|
|
3
|
+
"buildId": "run-278-1",
|
|
4
|
+
"commitSha": "eb4ff94a05a224818f6e1e2c273fbf00e6b9b76c",
|
|
5
|
+
"buildTime": "2026-11-01T21:49:32Z",
|
|
6
6
|
"serviceName": "muggle-ai-works-mcp"
|
|
7
7
|
}
|
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.31.0-staging.
|
|
4
|
+
"version": "5.31.0-staging.278",
|
|
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",
|
|
@@ -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.31.0-staging.
|
|
4
|
+
"version": "5.31.0-staging.278",
|
|
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.31.0-staging.
|
|
3
|
+
"version": "5.31.0-staging.278",
|
|
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.31.0-staging.
|
|
5
|
+
"version": "5.31.0-staging.278",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Muggle AI",
|
|
8
8
|
"email": "support@muggle-ai.com"
|