@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.
- package/README.md +8 -0
- package/dist/{chunk-LVUJ4E62.js → chunk-MSPMXBO5.js} +638 -468
- package/dist/{chunk-5PYK7DWB.js → chunk-WUMAJOBT.js} +17 -6
- package/dist/cli.js +2 -2
- package/dist/index.js +2 -2
- package/dist/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/plugin/.cursor-plugin/plugin.json +1 -1
- package/dist/plugin/README.md +19 -13
- package/dist/plugin/agents/test-prepare-runner.md +4 -1
- package/dist/plugin/agents/visual-walkthrough-builder.md +12 -5
- package/dist/plugin/commands/mtest.md +1 -1
- package/dist/plugin/hooks/README.md +15 -3
- package/dist/plugin/hooks/hooks.json +79 -0
- package/dist/plugin/scripts/ensure-electron-app.sh +62 -47
- package/dist/plugin/scripts/guardrail-classify-gate.sh +23 -0
- package/dist/plugin/scripts/guardrail-debug-path-gate.sh +41 -0
- package/dist/plugin/scripts/guardrail-pr-terminal.sh +6 -2
- package/dist/plugin/scripts/guardrail-record-stage-read.sh +21 -0
- package/dist/plugin/scripts/guardrail-record-stage-signals.sh +23 -0
- package/dist/plugin/scripts/guardrail-record-tests.sh +11 -4
- package/dist/plugin/scripts/guardrail-report-format.sh +9 -5
- package/dist/plugin/scripts/guardrail-skill-stages.sh +35 -0
- package/dist/plugin/scripts/guardrail-stage-gate.sh +43 -0
- package/dist/plugin/scripts/guardrail-walkthrough-gate.sh +41 -0
- package/dist/plugin/scripts/guardrails.mjs +474 -80
- package/dist/plugin/scripts/pr-watch-events.sh +116 -0
- package/dist/plugin/scripts/pr-watch-guards.sh +8 -2
- package/dist/plugin/scripts/pr-watch-loop.sh +254 -0
- package/dist/plugin/scripts/reconcile-stale-watchers.sh +66 -18
- package/dist/plugin/scripts/sign-body.sh +88 -0
- package/dist/plugin/skills/CLAUDE.md +16 -0
- package/dist/plugin/skills/_shared/post-merge-cleanup.md +83 -3
- package/dist/plugin/skills/_shared/pr-followup-helpers/loop-signature.md +6 -8
- package/dist/plugin/skills/_shared/pr-followup-helpers/reply-routing.md +10 -18
- package/dist/plugin/skills/_shared/resolve-e2e-validation-context.md +1 -0
- package/dist/plugin/skills/_shared/vcs/CLAUDE.md +1 -0
- package/dist/plugin/skills/_shared/vcs/github/pr-edit.md +3 -2
- package/dist/plugin/skills/_shared/vcs/github/reply-line-comment.md +3 -2
- package/dist/plugin/skills/_shared/vcs/github/top-level-comment.md +3 -2
- package/dist/plugin/skills/_shared/vcs/gitlab/mr-create.md +4 -1
- package/dist/plugin/skills/_shared/vcs/gitlab/mr-edit.md +4 -1
- package/dist/plugin/skills/_shared/vcs/gitlab/mr-note.md +4 -1
- package/dist/plugin/skills/_shared/vcs/gitlab/reply-discussion.md +4 -1
- package/dist/plugin/skills/_shared/vcs/post-signature.md +23 -12
- package/dist/plugin/skills/do/open-prs/forward.md +2 -2
- package/dist/plugin/skills/do/per-comment-replies.md +2 -15
- package/dist/plugin/skills/muggle/SKILL.md +1 -1
- package/dist/plugin/skills/muggle-browser-task/SKILL.md +1 -1
- package/dist/plugin/skills/muggle-pr-followup/CLAUDE.md +2 -1
- package/dist/plugin/skills/muggle-pr-followup/SKILL.md +6 -3
- package/dist/plugin/skills/muggle-pr-followup/adopt.md +60 -0
- package/dist/plugin/skills/muggle-pr-followup/arm-watcher.md +10 -14
- package/dist/plugin/skills/muggle-pr-followup/auto-track.md +2 -0
- package/dist/plugin/skills/muggle-pr-followup/evals/evals.json +63 -1
- package/dist/plugin/skills/muggle-pr-followup/output-templates/help.md +11 -1
- package/dist/plugin/skills/muggle-pr-followup/output-templates/inline-reply.md +1 -7
- package/dist/plugin/skills/muggle-pr-followup/output-templates/resolve-reminder.md +1 -4
- package/dist/plugin/skills/muggle-pr-followup/reconcile.md +32 -6
- package/dist/plugin/skills/muggle-pr-followup/state-schemas.md +18 -0
- package/dist/plugin/skills/muggle-preferences/SKILL.md +1 -1
- package/dist/plugin/skills/muggle-preferences/evals/evals.json +45 -0
- package/dist/plugin/skills/muggle-preferences/ops/change-one.md +1 -1
- package/dist/plugin/skills/muggle-preferences/ops/configure.md +4 -5
- package/dist/plugin/skills/muggle-preferences/ops/list.md +1 -1
- package/dist/plugin/skills/muggle-preferences/ops/set.md +3 -4
- package/dist/plugin/skills/muggle-preferences/preference-gates/README.md +3 -3
- package/dist/plugin/skills/muggle-preferences/preference-gates/autoSelectLocalHost.md +2 -2
- package/dist/plugin/skills/muggle-preferences/preference-gates/autoSelectProject.md +1 -1
- package/dist/plugin/skills/muggle-preferences/preference-gates/watcherLifetime.md +23 -0
- package/dist/plugin/skills/muggle-test/SKILL.md +23 -3
- package/dist/plugin/skills/muggle-test/execute-local.md +2 -0
- package/dist/plugin/skills/muggle-test-feature-local/SKILL.md +6 -1
- package/dist/plugin/skills/muggle-test-import/SKILL.md +1 -1
- package/dist/plugin/skills/muggle-test-prepare/SKILL.md +28 -15
- package/dist/plugin/skills/muggle-test-prepare/steps/check-running.md +1 -1
- package/dist/plugin/skills/muggle-test-prepare/steps/confirm-recipe.md +56 -0
- package/dist/plugin/skills/muggle-test-prepare/steps/derive-service-graph.md +51 -0
- package/dist/plugin/skills/muggle-test-prepare/steps/e2e-instructions.md +111 -0
- package/dist/plugin/skills/muggle-test-prepare/steps/identify-services.md +4 -2
- package/dist/plugin/skills/muggle-test-prepare/steps/readiness-report.md +12 -5
- package/dist/plugin/skills/muggle-test-prepare/steps/record-resolution.md +32 -0
- package/dist/plugin/skills/muggle-test-prepare/steps/replay-or-learn.md +20 -0
- package/dist/plugin/skills/muggle-test-prepare/steps/reuse-plan.md +9 -6
- package/dist/plugin/skills/muggle-test-prepare/steps/smoke-test.md +5 -1
- package/dist/plugin/skills/muggle-test-prepare/steps/start-services.md +3 -1
- package/dist/plugin/skills/muggle-test-regenerate-missing/SKILL.md +1 -1
- package/dist/release-manifest.json +4 -4
- package/dist/src-B5OTAZKM.js +1 -0
- package/package.json +7 -6
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.cursor-plugin/plugin.json +1 -1
- package/plugin/README.md +19 -13
- package/plugin/agents/test-prepare-runner.md +4 -1
- package/plugin/agents/visual-walkthrough-builder.md +12 -5
- package/plugin/commands/mtest.md +1 -1
- package/plugin/hooks/README.md +15 -3
- package/plugin/hooks/hooks.json +79 -0
- package/plugin/scripts/ensure-electron-app.sh +62 -47
- package/plugin/scripts/guardrail-classify-gate.sh +23 -0
- package/plugin/scripts/guardrail-debug-path-gate.sh +41 -0
- package/plugin/scripts/guardrail-pr-terminal.sh +6 -2
- package/plugin/scripts/guardrail-record-stage-read.sh +21 -0
- package/plugin/scripts/guardrail-record-stage-signals.sh +23 -0
- package/plugin/scripts/guardrail-record-tests.sh +11 -4
- package/plugin/scripts/guardrail-report-format.sh +9 -5
- package/plugin/scripts/guardrail-skill-stages.sh +35 -0
- package/plugin/scripts/guardrail-stage-gate.sh +43 -0
- package/plugin/scripts/guardrail-walkthrough-gate.sh +41 -0
- package/plugin/scripts/guardrails.mjs +474 -80
- package/plugin/scripts/pr-watch-events.sh +116 -0
- package/plugin/scripts/pr-watch-guards.sh +8 -2
- package/plugin/scripts/pr-watch-loop.sh +254 -0
- package/plugin/scripts/reconcile-stale-watchers.sh +66 -18
- package/plugin/scripts/sign-body.sh +88 -0
- package/plugin/skills/CLAUDE.md +16 -0
- package/plugin/skills/_shared/post-merge-cleanup.md +83 -3
- package/plugin/skills/_shared/pr-followup-helpers/loop-signature.md +6 -8
- package/plugin/skills/_shared/pr-followup-helpers/reply-routing.md +10 -18
- package/plugin/skills/_shared/resolve-e2e-validation-context.md +1 -0
- package/plugin/skills/_shared/vcs/CLAUDE.md +1 -0
- package/plugin/skills/_shared/vcs/github/pr-edit.md +3 -2
- package/plugin/skills/_shared/vcs/github/reply-line-comment.md +3 -2
- package/plugin/skills/_shared/vcs/github/top-level-comment.md +3 -2
- package/plugin/skills/_shared/vcs/gitlab/mr-create.md +4 -1
- package/plugin/skills/_shared/vcs/gitlab/mr-edit.md +4 -1
- package/plugin/skills/_shared/vcs/gitlab/mr-note.md +4 -1
- package/plugin/skills/_shared/vcs/gitlab/reply-discussion.md +4 -1
- package/plugin/skills/_shared/vcs/post-signature.md +23 -12
- package/plugin/skills/do/open-prs/forward.md +2 -2
- package/plugin/skills/do/per-comment-replies.md +2 -15
- package/plugin/skills/muggle/SKILL.md +1 -1
- package/plugin/skills/muggle-browser-task/SKILL.md +1 -1
- package/plugin/skills/muggle-pr-followup/CLAUDE.md +2 -1
- package/plugin/skills/muggle-pr-followup/SKILL.md +6 -3
- package/plugin/skills/muggle-pr-followup/adopt.md +60 -0
- package/plugin/skills/muggle-pr-followup/arm-watcher.md +10 -14
- package/plugin/skills/muggle-pr-followup/auto-track.md +2 -0
- package/plugin/skills/muggle-pr-followup/evals/evals.json +63 -1
- package/plugin/skills/muggle-pr-followup/output-templates/help.md +11 -1
- package/plugin/skills/muggle-pr-followup/output-templates/inline-reply.md +1 -7
- package/plugin/skills/muggle-pr-followup/output-templates/resolve-reminder.md +1 -4
- package/plugin/skills/muggle-pr-followup/reconcile.md +32 -6
- package/plugin/skills/muggle-pr-followup/state-schemas.md +18 -0
- package/plugin/skills/muggle-preferences/SKILL.md +1 -1
- package/plugin/skills/muggle-preferences/evals/evals.json +45 -0
- package/plugin/skills/muggle-preferences/ops/change-one.md +1 -1
- package/plugin/skills/muggle-preferences/ops/configure.md +4 -5
- package/plugin/skills/muggle-preferences/ops/list.md +1 -1
- package/plugin/skills/muggle-preferences/ops/set.md +3 -4
- package/plugin/skills/muggle-preferences/preference-gates/README.md +3 -3
- package/plugin/skills/muggle-preferences/preference-gates/autoSelectLocalHost.md +2 -2
- package/plugin/skills/muggle-preferences/preference-gates/autoSelectProject.md +1 -1
- package/plugin/skills/muggle-preferences/preference-gates/watcherLifetime.md +23 -0
- package/plugin/skills/muggle-test/SKILL.md +23 -3
- package/plugin/skills/muggle-test/execute-local.md +2 -0
- package/plugin/skills/muggle-test-feature-local/SKILL.md +6 -1
- package/plugin/skills/muggle-test-import/SKILL.md +1 -1
- package/plugin/skills/muggle-test-prepare/SKILL.md +28 -15
- package/plugin/skills/muggle-test-prepare/steps/check-running.md +1 -1
- package/plugin/skills/muggle-test-prepare/steps/confirm-recipe.md +56 -0
- package/plugin/skills/muggle-test-prepare/steps/derive-service-graph.md +51 -0
- package/plugin/skills/muggle-test-prepare/steps/e2e-instructions.md +111 -0
- package/plugin/skills/muggle-test-prepare/steps/identify-services.md +4 -2
- package/plugin/skills/muggle-test-prepare/steps/readiness-report.md +12 -5
- package/plugin/skills/muggle-test-prepare/steps/record-resolution.md +32 -0
- package/plugin/skills/muggle-test-prepare/steps/replay-or-learn.md +20 -0
- package/plugin/skills/muggle-test-prepare/steps/reuse-plan.md +9 -6
- package/plugin/skills/muggle-test-prepare/steps/smoke-test.md +5 -1
- package/plugin/skills/muggle-test-prepare/steps/start-services.md +3 -1
- package/plugin/skills/muggle-test-regenerate-missing/SKILL.md +1 -1
- package/dist/src-FDK2KQSZ.js +0 -1
|
@@ -11,7 +11,9 @@ Capture the PID. Write all service entries to `/tmp/muggle-test-prepare.json`.
|
|
|
11
11
|
|
|
12
12
|
**Startup verification** — confirm PID alive (`kill -0 <pid> 2>/dev/null`), then run the two-stage readiness probe per [`../../_shared/dev-server-readiness.md`](../../_shared/dev-server-readiness.md) against the log. Cap log-tail at 60 s. Halt on whatever surfaces.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
A boot that takes most of the 60 s cap but keeps making progress is a property of this stack, not a fault — record it per [record-resolution](./record-resolution.md) so the next run expects the wait instead of treating it as a hang.
|
|
15
|
+
|
|
16
|
+
If a PID dies immediately, consult the recipe's recorded resolutions for this service first; apply a match without asking. Otherwise show the last 20 log lines:
|
|
15
17
|
|
|
16
18
|
> "**backend-api** exited right after starting. Here's the tail of its log:"
|
|
17
19
|
|
|
@@ -65,7 +65,7 @@ If auth keeps failing, suggest the user run `muggle logout && muggle login` from
|
|
|
65
65
|
|
|
66
66
|
A **project** is the unit on the Muggle AI dashboard that groups test cases, scripts, and runs. The user must pick the one to scan — never auto-select from repo name, branch, or URL heuristics.
|
|
67
67
|
|
|
68
|
-
The per-repo project cache lives
|
|
68
|
+
The per-repo project cache lives in `~/.muggle-ai/last-project.json`, in the entry keyed on `<cwd>` (via the `muggle-local-last-project-get` / `muggle-local-last-project-set` MCP tools). Look for `Muggle Test Last Project: id=… url=… name="…"` in session context.
|
|
69
69
|
|
|
70
70
|
Gate `autoSelectProject` (per `preference-gates/README.md`). Cache: `Muggle Test Last Project` session line.
|
|
71
71
|
- `always` + cache → use cached `projectId`, proceed to Step 3. No cache → fall through to `ask`.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"release": "5.
|
|
3
|
-
"buildId": "run-
|
|
4
|
-
"commitSha": "
|
|
5
|
-
"buildTime": "2026-08-
|
|
2
|
+
"release": "5.11.0",
|
|
3
|
+
"buildId": "run-71-1",
|
|
4
|
+
"commitSha": "c63bb23b7fe35c075d6c973c07b2ac8315f274e4",
|
|
5
|
+
"buildTime": "2026-08-14T06:33:25Z",
|
|
6
6
|
"serviceName": "muggle-ai-works-mcp"
|
|
7
7
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { DEFAULT_PREFERENCES, PREFERENCES_FILE_NAME, PREFERENCES_PROJECT_DIR_NAME, PREFERENCES_SCHEMA, PREFERENCES_VERSION, PREFERENCE_ALLOWED_VALUES, PreferenceKey, PreferenceValue, ProjectPreferencesReconcileOutcome, WATCHER_LIFETIME_SECONDS, WATCHER_LIFETIME_UNBOUNDED_SECONDS, buildElectronAppChecksumsUrl, buildElectronAppReleaseAssetUrl, buildElectronAppReleaseTag, calculateFileChecksum, createApiKeyWithToken, createChildLogger, deleteApiKeyData, deleteCredentials, e2e_exports as e2e, formatPreferencesOneLiner, getApiKey, getApiKeyFilePath, getAuthService, getBundledElectronAppVersion, getCallerCredentials, getCallerCredentialsAsync, getChecksumForPlatform, getConfig, getCredentialsFilePath, getDataDir, getDownloadBaseUrl, getElectronAppChecksums, getElectronAppDir, getElectronAppVersion, getElectronAppVersionSource, getLocalQaTools, getLogger, getPlatformKey, getQaTools, getValidApiKeyData, getValidCredentials, hasApiKey, isElectronAppInstalled, isFirstRun, loadApiKeyData, loadCredentials, local_exports as localQa, mcp_exports as mcp, openBrowserUrl, performLogin, performLogout, pollDeviceCode, e2e_exports as qa, reconcileProjectPreferences, resetConfig, resetLogger, resetPreference, resolveElectronAppPathOrNull, resolvePreferences, saveApiKey, saveApiKeyData, saveCredentials, startDeviceCodeFlow, toolRequiresAuth, validatePreference, verifyFileChecksum, writePreferences } from './chunk-MSPMXBO5.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.11.0",
|
|
5
5
|
"description": "Ship quality products with AI-powered E2E acceptance testing that validates your web app like a real user — from Claude Code and Cursor to PR.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/index.js",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"verify:plugin": "node scripts/verify-plugin-marketplace.mjs",
|
|
24
24
|
"verify:contracts": "node scripts/verify-compatibility-contracts.mjs",
|
|
25
25
|
"verify:skill-deps": "node scripts/check-skill-deps.mjs",
|
|
26
|
+
"verify:signatures": "node scripts/check-post-signatures.mjs",
|
|
26
27
|
"smoke:cli": "node scripts/smoke-cli.mjs",
|
|
27
28
|
"verify:electron-release-checksums": "node scripts/verify-electron-release-checksums.mjs",
|
|
28
29
|
"verify:upgrade-experience": "node scripts/verify-upgrade-experience.mjs",
|
|
@@ -47,14 +48,14 @@
|
|
|
47
48
|
"eval:studio-gen": "tsx internal/studio-gen-eval/src/run.ts"
|
|
48
49
|
},
|
|
49
50
|
"muggleConfig": {
|
|
50
|
-
"electronAppVersion": "1.
|
|
51
|
+
"electronAppVersion": "1.9.0",
|
|
51
52
|
"downloadBaseUrl": "https://github.com/multiplex-ai/muggle-ai-works/releases/download",
|
|
52
53
|
"runtimeTargetDefault": "production",
|
|
53
54
|
"checksums": {
|
|
54
|
-
"darwin-arm64": "
|
|
55
|
-
"darwin-x64": "
|
|
56
|
-
"linux-x64": "
|
|
57
|
-
"win32-x64": "
|
|
55
|
+
"darwin-arm64": "1baa38bde71b74984705bcac3e5a42a7a158a7b0574dab496f3e240846c0ac65",
|
|
56
|
+
"darwin-x64": "098cc2db6f32c7857bd933512320524d213804b378dc8bf0ade791396f058041",
|
|
57
|
+
"linux-x64": "d434a88a087c7410785a443cfe03102322fa7a295b77600f5ed92e2113e271c2",
|
|
58
|
+
"win32-x64": "0aecbc01010ab0a268137ed31bcaaa05fe9215daa9f4c0e4871b07554d28c0c8"
|
|
58
59
|
}
|
|
59
60
|
},
|
|
60
61
|
"dependencies": {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "muggle",
|
|
3
3
|
"description": "Run real-browser end-to-end (E2E) acceptance tests on your web app from any AI coding agent. Generate test scripts from plain English, replay them on localhost, capture screenshots, and validate user flows like signup, checkout, and dashboards. Works across Claude Code, Cursor, Codex, and Windsurf.",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.11.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Muggle AI",
|
|
7
7
|
"email": "support@muggle-ai.com"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "muggle",
|
|
3
3
|
"displayName": "Muggle AI",
|
|
4
4
|
"description": "Ship quality products with AI-powered end-to-end (E2E) acceptance testing that validates your web app like a real user — from Claude Code and Cursor to PR.",
|
|
5
|
-
"version": "5.
|
|
5
|
+
"version": "5.11.0",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Muggle AI",
|
|
8
8
|
"email": "support@muggle-ai.com"
|
package/plugin/README.md
CHANGED
|
@@ -21,19 +21,25 @@ This updates the CLI, configures Cursor MCP (`~/.cursor/mcp.json`), and syncs `m
|
|
|
21
21
|
|
|
22
22
|
Type `muggle` to discover the full command family.
|
|
23
23
|
|
|
24
|
-
| Skill | What it does |
|
|
25
|
-
|
|
26
|
-
| `/muggle:muggle` | Router and menu for all Muggle Test commands. |
|
|
27
|
-
| `/muggle:muggle-do` | Autonomous dev pipeline: requirements, code, unit tests, E2E acceptance tests, PR. |
|
|
28
|
-
| `/muggle:muggle-test` | Change-driven E2E acceptance router: detects code changes, maps to use cases, runs test generation locally or remotely, publishes to dashboard, opens in browser, posts E2E acceptance results to PR. |
|
|
29
|
-
| `/muggle:muggle-test-feature-local` | Test a feature on localhost with AI-driven browser automation. Offers publish to cloud after each run. |
|
|
30
|
-
| `/muggle:muggle-test-
|
|
31
|
-
| `/muggle:muggle-test-
|
|
32
|
-
| `/muggle:muggle-
|
|
33
|
-
| `/muggle:muggle-
|
|
34
|
-
| `/muggle:muggle-
|
|
35
|
-
|
|
36
|
-
|
|
24
|
+
| Skill | Shorthand | What it does |
|
|
25
|
+
|:---|:---|:---|
|
|
26
|
+
| `/muggle:muggle` | `/m` | Router and menu for all Muggle Test commands. |
|
|
27
|
+
| `/muggle:muggle-do` | `/mdo` | Autonomous dev pipeline: requirements, code, unit tests, E2E acceptance tests, PR. |
|
|
28
|
+
| `/muggle:muggle-test` | `/mtest` | Change-driven E2E acceptance router: detects code changes, maps to use cases, runs test generation locally or remotely, publishes to dashboard, opens in browser, posts E2E acceptance results to PR. |
|
|
29
|
+
| `/muggle:muggle-test-feature-local` | `/mtestlocal` | Test a feature on localhost with AI-driven browser automation. Offers publish to cloud after each run. |
|
|
30
|
+
| `/muggle:muggle-test-prepare` | `/mtestprep` | Verify the dev servers and sibling services a test run needs, and start whatever is missing. |
|
|
31
|
+
| `/muggle:muggle-test-import` | `/mimport` | Import existing tests into Muggle Test — from Playwright/Cypress specs, PRDs, Gherkin feature files, test plan docs, or any test artifact. |
|
|
32
|
+
| `/muggle:muggle-test-regenerate-missing` | `/mregen` | Bulk-regenerate test scripts for every test case in a project that doesn't currently have an active script. Scans DRAFT + GENERATION_PENDING, confirms the list with the user, and dispatches remote generation workflows for each. |
|
|
33
|
+
| `/muggle:muggle-browser-task` | `/mbt` | Perform a real action on a website from plain English — log in and submit the form, create the ticket, refund the charge. |
|
|
34
|
+
| `/muggle:muggle-pr-visual-walkthrough` | `/mpr` | Post per-test-case dashboard links, step-by-step screenshots, and a pass/fail summary to a PR. |
|
|
35
|
+
| `/muggle:muggle-pr-followup` | `/mprfollowup` | Watch one PR's review thread and dispatch the work to address incoming feedback. |
|
|
36
|
+
| `/muggle:muggle-feedback` | `/mfeedback` | Flag a generated action script, or one step in it, as wrong so Muggle can analyze and regenerate affected scripts. |
|
|
37
|
+
| `/muggle:muggle-preferences` | `/mprefs` | View, set, or reset the preferences that gate Muggle Test behavior. |
|
|
38
|
+
| `/muggle:muggle-status` | `/mstatus` | Health check for Electron browser test runner, MCP server, and authentication. |
|
|
39
|
+
| `/muggle:muggle-repair` | `/mrepair` | Diagnose and fix broken installation automatically. |
|
|
40
|
+
| `/muggle:muggle-upgrade` | `/mupgrade` | Update Electron browser test runner and MCP server to latest version. |
|
|
41
|
+
|
|
42
|
+
Every skill ships the short alias in the table above. Type `/m` (Claude Code) or `m` (Cursor) to open the menu, or jump straight to one (e.g. `/mtest`). Claude Code namespaces plugin commands, so the aliases resolve as `/muggle:mtest`; mirror them into `~/.claude/commands/` to type `/mtest` bare.
|
|
37
43
|
|
|
38
44
|
## MCP Tools
|
|
39
45
|
|
|
@@ -16,6 +16,7 @@ The dispatch prompt carries the resolved prepare plan:
|
|
|
16
16
|
- `testingScope` and `excludedServices[]` (with reasons).
|
|
17
17
|
- The recorded dev-server URL (from the `autoSelectLocalHost` resolution) — never invent or default a host/port; a framework default like `:3000` is not a fallback.
|
|
18
18
|
- Resolved gate values the stages read (`autoRebase` outcome already applied or explicitly skipped upstream).
|
|
19
|
+
- The E2E run instructions — startup order, manual steps, local gotchas — already captured from the user. Honour the recorded startup order when starting services, and write the instructions out per the readiness-report stage. Absent means the skill had nothing to record; never invent them.
|
|
19
20
|
|
|
20
21
|
## Stages
|
|
21
22
|
|
|
@@ -37,7 +38,9 @@ Track every launched process in `/tmp/muggle-test-prepare.json` exactly per the
|
|
|
37
38
|
|
|
38
39
|
## Output contract
|
|
39
40
|
|
|
40
|
-
Return the readiness-report table verbatim as your report, prefixed by one line: `READY` (all services green), `DEGRADED: <which service, why>` (something is up but failed its smoke test after the clean-restart loop), or `needs-input: <decision>`.
|
|
41
|
+
Return the readiness-report table verbatim as your report, prefixed by one line: `READY` (all services green), `DEGRADED: <which service, why>` (something is up but failed its smoke test after the clean-restart loop), or `needs-input: <decision>`.
|
|
42
|
+
|
|
43
|
+
Also return every problem you hit and what cleared it — signal, service, resolution — per [`../skills/muggle-test-prepare/steps/record-resolution.md`](../skills/muggle-test-prepare/steps/record-resolution.md). On a learning run the dispatcher folds these into the recipe gate; on a replay they confirm the recipe still holds. A problem you could not clear is not a resolution: report it as an exclusion with its reason. You persist none of this yourself — the dispatcher owns every write. The dispatcher relays this to its caller — other skills gate on it, so a wrong `READY` is expensive; when in doubt between READY and DEGRADED, pick DEGRADED and say why. `needs-input:` is only for a decision the plan failed to resolve (a missing URL, an unapproved start, an unknown directory) — a service that stays broken after the loop is `DEGRADED` with the diagnosis, never `needs-input:`, even when no further automated fix exists. Repairing the app's own source code is out of scope entirely: a source-level bug surfaced by the smoke test is a `DEGRADED` diagnosis to report, not a decision to escalate.
|
|
41
44
|
|
|
42
45
|
## Guardrails
|
|
43
46
|
|
|
@@ -31,15 +31,22 @@ echo "$REPORT_JSON" | muggle build-pr-section > /tmp/muggle-pr-section.json
|
|
|
31
31
|
|
|
32
32
|
## Deliver
|
|
33
33
|
|
|
34
|
-
**Mode A (`post`)** —
|
|
34
|
+
**Mode A (`post`)** — deliver `body`, then `comment` only if non-null. Sign each posted body per [`../skills/_shared/vcs/post-signature.md`](../skills/_shared/vcs/post-signature.md) with `--mode plain` — this post is the walkthrough's own, so the command it names is `/muggle-pr-visual-walkthrough`.
|
|
35
|
+
|
|
36
|
+
**Update in place when this PR already carries a walkthrough.** A rerun after a failure must leave the PR with **one** walkthrough reflecting latest state, not a comment per attempt. Resolve which comment to update by reading the PR — never by remembering an id — so the behavior is idempotent across sessions and survives a lost session or a forgotten handle:
|
|
35
37
|
|
|
36
38
|
```bash
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
sign() { bash "${CLAUDE_PLUGIN_ROOT}/scripts/sign-body.sh" --command /muggle-pr-visual-walkthrough --mode plain; }
|
|
40
|
+
existing=$(gh api "repos/<owner>/<repo>/issues/<prNumber>/comments" \
|
|
41
|
+
--jq '[.[] | select(.body | contains("muggle-pr-section")) | .id] | join(" ")')
|
|
40
42
|
```
|
|
41
43
|
|
|
42
|
-
|
|
44
|
+
- `existing` empty → post fresh: `jq -r '.body' … | sign | gh pr comment <prNumber> --body-file -`, then the same for `.comment` when non-null.
|
|
45
|
+
- `existing` non-empty → update the first id with `body` via `gh api --method PATCH repos/<owner>/<repo>/issues/comments/<id> -F body=@-`, feeding the same signed text on stdin. Handle `comment` against the second id when both exist; post it fresh when the overflow is new, and delete a now-surplus overflow comment (`gh api --method DELETE …`) so a stale tail never outlives the run it described.
|
|
46
|
+
|
|
47
|
+
Match only comments carrying the sentinel — never every comment the loop user wrote — so an unrelated reply is never overwritten.
|
|
48
|
+
|
|
49
|
+
Report back: PR URL, whether an overflow comment was involved, and whether this was a fresh post or an update.
|
|
43
50
|
|
|
44
51
|
**Modes B/C (`render-for-new-pr` / `embed`)** — do not post, do not touch `gh`. Return the CLI output verbatim as your report:
|
|
45
52
|
|
package/plugin/commands/mtest.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
2
|
+
description: Real-browser E2E on your changes — no Playwright code, screenshots on the PR (alias for /muggle-test)
|
|
3
3
|
argument-hint: [optional context, e.g. "on staging"]
|
|
4
4
|
allowed-tools: [Skill]
|
|
5
5
|
---
|
package/plugin/hooks/README.md
CHANGED
|
@@ -26,24 +26,36 @@ Enforcement is reserved for the handoffs that were being skipped: the E2E accept
|
|
|
26
26
|
|
|
27
27
|
Each guardrail is a thin bash wrapper in `../scripts/` registered in `hooks.json`. The wrapper pipes the event payload (stdin JSON) to the bundled `../scripts/guardrails.mjs <subcommand>`, which holds the decision logic (built from `src/guardrails/`, vitest-covered). Per-session state in `~/.muggle-ai/guardrails/<session_id>.json` tracks what fired. Any *failure* degrades to `{}` (allow) — a gate blocks only by an explicit, tested decision, never by accident.
|
|
28
28
|
|
|
29
|
+
Each wrapper short-circuits in shell first, so the common case never pays Node cold-start. That pre-filter is a second, looser copy of what `guardrails.mjs` matches, and it is the one place a guardrail can fail *silently*: a payload it drops — a skip marker, a reopen line, a comment edit — reaches no recorder, and the gate keeps demanding an action the user already took. Over-matching is free; under-matching is a dead escape hatch. `src/test/guardrails/hook-prefilter.test.ts` pins every payload each subcommand acts on against the wrapper guarding it, and derives the skip-marker tokens from source so a new marker is covered the moment it exists.
|
|
30
|
+
|
|
29
31
|
## Guardrails
|
|
30
32
|
|
|
31
33
|
| Hook event | Wrapper | Strength | Condition | Preference | Effect |
|
|
32
34
|
| :--------- | :------ | :------- | :-------- | :--------- | :----- |
|
|
33
35
|
| `PostToolUse` (Bash) | `guardrail-pr-opened.sh` | advise | a `gh pr create`/`gh pr ready` just succeeded | `autoWatchPR` | start a `muggle-pr-followup` watcher on the new PR |
|
|
34
|
-
| `PostToolUse` (Bash + muggle execute/replay/skill-emit MCP tools) | `guardrail-record-tests.sh` | record | a unit-test command passed, an E2E run happened (execute/replay call, or the muggle-test skill's own telemetry emit — which registers a clean SKIP verdict too), or an `echo "MUGGLE_E2E_SKIP: <reason>"` marker declared E2E un-runnable | — | set `unitTestsGreen` / `e2eRun` / `e2eSkipped` session state |
|
|
36
|
+
| `PostToolUse` (Bash + muggle execute/replay/skill-emit MCP tools) | `guardrail-record-tests.sh` | record | a unit-test command passed, an E2E run happened (execute/replay call, or the muggle-test skill's own telemetry emit — which registers a clean SKIP verdict too), or an `echo "MUGGLE_E2E_SKIP: <reason>"` marker declared E2E un-runnable, or a walkthrough carrying the `build-pr-section` sentinel was posted / declared un-postable via `echo "MUGGLE_WALKTHROUGH_SKIP: <reason>"` | — | set `unitTestsGreen` / `e2eRun` / `e2eSkipped` / `walkthroughPosted` / `walkthroughSkipped` session state |
|
|
35
37
|
| `PostToolUse` (Bash + Monitor) | `guardrail-pr-terminal.sh` | advise | a PR just went terminal — a `gh pr merge`/`gh pr close` success line or the watch monitor's `TERMINAL pr=N` exit line (never bare `"state":"MERGED"` metadata) | — | record `terminalPending`, direct the post-merge handoff: finalize the watcher slot, tear down per `autoCleanup`, offer next options via AskUserQuestion |
|
|
36
38
|
| `PostToolUse` (AskUserQuestion) | `guardrail-offer-ran.sh` | record | a next-options offer ran while a terminal PR was pending | — | clear `terminalPending` — the only exit for the post-merge Stop gate |
|
|
37
|
-
| `
|
|
39
|
+
| `PostToolUse` (Skill + muggle telemetry-skill-emit) | `guardrail-skill-stages.sh` | advise | a skill this plugin ships was invoked; its `SKILL.md` frontmatter may declare `mandatoryStages`. Registered on the skill's own MCP announcement too, so the declaration still lands if the Skill payload ever stops carrying a name | — | record `lastInvokedSkillName` + `mandatoryStages`, and name the declared files as required reading at the moment of use |
|
|
40
|
+
| `PostToolUse` (Read) | `guardrail-record-stage-read.sh` | record | a markdown file under a `skills/` directory was opened | — | append to `stagesRead` — how the stage gate tells a stage that was read from one that was skipped |
|
|
41
|
+
| `PostToolUse` (Bash + muggle telemetry-event-emit / user-feedback-create) | `guardrail-record-stage-signals.sh` | record | a Step 6f `pre-execution-classification` emit, debug-path evidence for a failed run (a `*-failure-classified\|resolved` emit or feedback naming it), or a `MUGGLE_STAGE_SKIP` / `MUGGLE_CLASSIFY_SKIP` / `MUGGLE_DEBUG_SKIP` marker | — | set `classifiedTestCaseIds` / `debuggedRuns` / the matching skip flag |
|
|
42
|
+
| `PreToolUse` (Bash) | `guardrail-report-format.sh` | **enforce** | a `gh pr comment\|create\|edit` body — or a `gh api … issues/comments/<id>` PATCH editing one — reads like an E2E report but lacks the `build-pr-section` sentinel | — | **deny** — render via `muggle build-pr-section` instead |
|
|
43
|
+
| `PreToolUse` (muggle local execute/replay) | `guardrail-classify-gate.sh` | **enforce** | `muggle-test` is the skill in play and the target test case has no `pre-execution-classification` this session | — | **deny** — run Step 6f first (it calls `muggle-remote-test-script-list`, which is where the run learns the case has never passed) or record a legitimate skip via `MUGGLE_CLASSIFY_SKIP`. Scoped to `muggle-test`, so the single-target skills that legitimately skip classification are untouched; fails open when the test case can't be resolved |
|
|
38
44
|
| `Stop` | `guardrail-e2e-gate.sh` | **enforce** | unit tests passed this session, no E2E ran yet, and no skip was recorded | `autoE2ETest` | **block** the turn until E2E runs via `muggle-test` or a `MUGGLE_E2E_SKIP` marker records a legitimate skip (full message once, one-line reminders after; releases after 3 blocks) |
|
|
39
45
|
| `Stop` | `guardrail-terminal-gate.sh` | **enforce** | a PR went terminal this session and the AskUserQuestion next-options offer hasn't run since | — | **block** the turn until the post-merge handoff runs (full message once, one-line reminders after; releases after 3 blocks; nothing but the offer resets the counter) |
|
|
46
|
+
| `Stop` | `guardrail-watch-gate.sh` | **enforce** | a PR opened this session that no `muggle-do` session slot tracks | `autoWatchPR` | **block** the turn until a slot is seeded via `muggle-pr-followup` or a `MUGGLE_WATCH_SKIP` marker records a legitimate skip (releases after 3 blocks) |
|
|
47
|
+
| `Stop` | `guardrail-walkthrough-gate.sh` | **enforce** | an E2E acceptance run happened this session and a PR in play — opened this session or on the working branch — carries no walkthrough in its body or comments | `postPRVisualWalkthrough` | **block** the turn until the walkthrough is posted via `muggle-pr-visual-walkthrough` or a `MUGGLE_WALKTHROUGH_SKIP` marker records a legitimate skip (releases after 3 blocks; an unreachable PR fails open to not-owed) |
|
|
48
|
+
| `Stop` | `guardrail-stage-gate.sh` | **enforce** | a skill invoked this session declared `mandatoryStages` and one of those files was never opened | — | **block** the turn until the stages are read or a `MUGGLE_STAGE_SKIP` marker records a legitimate skip (releases after 3 blocks) |
|
|
49
|
+
| `Stop` | `guardrail-debug-path-gate.sh` | **enforce** | a local execution returned a non-passing run this session and nothing routed it through `_shared/debug-failed-run.md` | — | **block** the turn until the run is diagnosed (its `*-failure-classified` emit or feedback naming it) or a `MUGGLE_DEBUG_SKIP: <runId> <reason>` marker clears that run (releases after 3 blocks) |
|
|
40
50
|
| `UserPromptSubmit` | `guardrail-build-router.sh` | advise | a build/implement/fix request (first one this session) | `autoRouteBuildToMuggleDo` | route the work through `muggle-do` (build delegated to superpowers) |
|
|
41
51
|
|
|
42
52
|
## Session-start reconcile nudge
|
|
43
53
|
|
|
44
54
|
`SessionStart` (`scripts/reconcile-stale-watchers.sh`) — a standalone advisory, not part of the `guardrails.mjs` decision tree above.
|
|
45
55
|
|
|
46
|
-
`muggle-pr-followup` watchers are session-only (a monitor or `/loop` cron); they die with their session, leaving open PRs with no live poller. This is by design — a review is addressed only inside a session that carries the context to address it. The skill's [`reconcile`](../skills/muggle-pr-followup/reconcile.md) procedure recovers them at the next session start — finalizes slots whose PR went terminal, sweeps orphan crons, re-arms silently-stopped open watchers — but re-arming needs Claude tools a shell hook can't call. So this hook nudges rather than acts
|
|
56
|
+
`muggle-pr-followup` watchers are session-only (a monitor or `/loop` cron); they die with their session, leaving open PRs with no live poller. This is by design — a review is addressed only inside a session that carries the context to address it. The skill's [`reconcile`](../skills/muggle-pr-followup/reconcile.md) procedure recovers them at the next session start — finalizes slots whose PR went terminal, sweeps orphan crons, re-arms silently-stopped open watchers — but re-arming needs Claude tools a shell hook can't call. So this hook nudges rather than acts.
|
|
57
|
+
|
|
58
|
+
The nudge counts only slots **this session owns**, and reports the rest as orphans it will not act on — ownership and the recovery rules it gates are defined in [`reconcile`](../skills/muggle-pr-followup/reconcile.md). Nothing owned and nothing orphaned → it emits nothing. A pure directory scan (no `gh`, no writes), so it's cheap enough for every session start.
|
|
47
59
|
|
|
48
60
|
## Session-start state GC
|
|
49
61
|
|
package/plugin/hooks/hooks.json
CHANGED
|
@@ -36,6 +36,17 @@
|
|
|
36
36
|
"timeout": 10
|
|
37
37
|
}
|
|
38
38
|
]
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"matcher": "mcp__.*muggle.*(execute-test-generation|execute-replay)",
|
|
42
|
+
"hooks": [
|
|
43
|
+
{
|
|
44
|
+
"type": "command",
|
|
45
|
+
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/scripts/guardrail-classify-gate.sh\"",
|
|
46
|
+
"async": false,
|
|
47
|
+
"timeout": 10
|
|
48
|
+
}
|
|
49
|
+
]
|
|
39
50
|
}
|
|
40
51
|
],
|
|
41
52
|
"PostToolUse": [
|
|
@@ -59,6 +70,12 @@
|
|
|
59
70
|
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/scripts/guardrail-pr-terminal.sh\"",
|
|
60
71
|
"async": false,
|
|
61
72
|
"timeout": 10
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"type": "command",
|
|
76
|
+
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/scripts/guardrail-record-stage-signals.sh\"",
|
|
77
|
+
"async": false,
|
|
78
|
+
"timeout": 10
|
|
62
79
|
}
|
|
63
80
|
]
|
|
64
81
|
},
|
|
@@ -73,6 +90,50 @@
|
|
|
73
90
|
}
|
|
74
91
|
]
|
|
75
92
|
},
|
|
93
|
+
{
|
|
94
|
+
"matcher": "mcp__.*muggle.*(telemetry-event-emit|user-feedback-create)",
|
|
95
|
+
"hooks": [
|
|
96
|
+
{
|
|
97
|
+
"type": "command",
|
|
98
|
+
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/scripts/guardrail-record-stage-signals.sh\"",
|
|
99
|
+
"async": false,
|
|
100
|
+
"timeout": 10
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"matcher": "Skill",
|
|
106
|
+
"hooks": [
|
|
107
|
+
{
|
|
108
|
+
"type": "command",
|
|
109
|
+
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/scripts/guardrail-skill-stages.sh\"",
|
|
110
|
+
"async": false,
|
|
111
|
+
"timeout": 10
|
|
112
|
+
}
|
|
113
|
+
]
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"matcher": "mcp__.*muggle.*telemetry-skill-emit",
|
|
117
|
+
"hooks": [
|
|
118
|
+
{
|
|
119
|
+
"type": "command",
|
|
120
|
+
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/scripts/guardrail-skill-stages.sh\"",
|
|
121
|
+
"async": false,
|
|
122
|
+
"timeout": 10
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"matcher": "Read",
|
|
128
|
+
"hooks": [
|
|
129
|
+
{
|
|
130
|
+
"type": "command",
|
|
131
|
+
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/scripts/guardrail-record-stage-read.sh\"",
|
|
132
|
+
"async": false,
|
|
133
|
+
"timeout": 10
|
|
134
|
+
}
|
|
135
|
+
]
|
|
136
|
+
},
|
|
76
137
|
{
|
|
77
138
|
"matcher": "Monitor",
|
|
78
139
|
"hooks": [
|
|
@@ -116,6 +177,24 @@
|
|
|
116
177
|
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/scripts/guardrail-watch-gate.sh\"",
|
|
117
178
|
"async": false,
|
|
118
179
|
"timeout": 10
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"type": "command",
|
|
183
|
+
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/scripts/guardrail-walkthrough-gate.sh\"",
|
|
184
|
+
"async": false,
|
|
185
|
+
"timeout": 15
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
"type": "command",
|
|
189
|
+
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/scripts/guardrail-stage-gate.sh\"",
|
|
190
|
+
"async": false,
|
|
191
|
+
"timeout": 10
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
"type": "command",
|
|
195
|
+
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/scripts/guardrail-debug-path-gate.sh\"",
|
|
196
|
+
"async": false,
|
|
197
|
+
"timeout": 10
|
|
119
198
|
}
|
|
120
199
|
]
|
|
121
200
|
}
|
|
@@ -110,6 +110,10 @@ version_check() {
|
|
|
110
110
|
version_check || true
|
|
111
111
|
|
|
112
112
|
# --- Preferences injection ---
|
|
113
|
+
# Preferences are user-level: defaults overlaid by the global file only. A legacy
|
|
114
|
+
# <cwd>/.muggle-ai/preferences.json is left on disk but no longer read, so the
|
|
115
|
+
# keys it can no longer apply are named once — the stamp file suppresses the
|
|
116
|
+
# repeat until that key set changes.
|
|
113
117
|
prefs_global_file="${HOME}/.muggle-ai/preferences.json"
|
|
114
118
|
prefs_line=""
|
|
115
119
|
prefs_file_note=""
|
|
@@ -119,8 +123,13 @@ if [ -f "$prefs_global_file" ]; then
|
|
|
119
123
|
# Uses node for reliable JSON parsing (already required for muggle).
|
|
120
124
|
prefs_line=$(node -e "
|
|
121
125
|
const fs = require('fs');
|
|
126
|
+
const os = require('os');
|
|
127
|
+
const path = require('path');
|
|
122
128
|
try {
|
|
123
|
-
|
|
129
|
+
// Resolved through node, not the shell's \$HOME: under Git Bash the shell
|
|
130
|
+
// reports a POSIX path that Windows node cannot open.
|
|
131
|
+
const globalFile = path.join(os.homedir(), '.muggle-ai', 'preferences.json');
|
|
132
|
+
const g = JSON.parse(fs.readFileSync(globalFile, 'utf-8')).preferences || {};
|
|
124
133
|
const defaults = {
|
|
125
134
|
autoLogin:'ask', autoSelectProject:'ask', autoSelectLocalHost:'ask',
|
|
126
135
|
showElectronBrowser:'ask', openTestResultsAfterRun:'ask',
|
|
@@ -131,15 +140,30 @@ if [ -f "$prefs_global_file" ]; then
|
|
|
131
140
|
autoUseWorktree:'ask', autoRebase:'ask', autoCleanup:'ask',
|
|
132
141
|
autoE2ETest:'always', autoRouteBuildToMuggleDo:'ask'
|
|
133
142
|
};
|
|
143
|
+
const resolved = { ...defaults, ...g };
|
|
144
|
+
const line = Object.entries(resolved).map(([k,v]) => k+'='+v).join(' ');
|
|
145
|
+
const blocks = ['Muggle Test Preferences (~/.muggle-ai/preferences.json):\\\\n' + line];
|
|
146
|
+
|
|
134
147
|
const cwd = process.env.CLAUDE_CWD || process.env.CURSOR_CWD || process.cwd();
|
|
135
|
-
const pPath =
|
|
148
|
+
const pPath = path.join(cwd, '.muggle-ai', 'preferences.json');
|
|
136
149
|
let p = {};
|
|
137
150
|
try { p = JSON.parse(fs.readFileSync(pPath, 'utf-8')).preferences || {}; } catch {}
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
151
|
+
const inertKeys = Object.keys(p).filter((k) => p[k] !== resolved[k]).sort();
|
|
152
|
+
if (inertKeys.length > 0) {
|
|
153
|
+
const stampDir = path.join(os.homedir(), '.cache', 'muggle');
|
|
154
|
+
const stampFile = path.join(stampDir, 'project-prefs-inert');
|
|
155
|
+
const stamp = cwd + '|' + inertKeys.join(',');
|
|
156
|
+
let lastStamp = '';
|
|
157
|
+
try { lastStamp = fs.readFileSync(stampFile, 'utf-8'); } catch {}
|
|
158
|
+
if (lastStamp !== stamp) {
|
|
159
|
+
try { fs.mkdirSync(stampDir, { recursive: true }); fs.writeFileSync(stampFile, stamp); } catch {}
|
|
160
|
+
blocks.push(
|
|
161
|
+
'Muggle Test: per-project preferences were removed — ' + pPath + ' is no longer read, so these keys no longer take effect: ' + inertKeys.join(', ') + '.\\\\n' +
|
|
162
|
+
'Tell the user to re-apply any they want everywhere with \`/muggle-preferences\`; the file is safe to delete.'
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
console.log(blocks.join('\\\\n\\\\n'));
|
|
143
167
|
} catch { console.log(''); }
|
|
144
168
|
" 2>/dev/null || true)
|
|
145
169
|
if [ -n "$prefs_line" ]; then
|
|
@@ -149,53 +173,44 @@ else
|
|
|
149
173
|
prefs_file_note="\\n\\nMuggle Test Preferences: not configured. Run \\\`muggle setup\\\` or tell the agent to set preferences."
|
|
150
174
|
fi
|
|
151
175
|
|
|
152
|
-
# --- Last-
|
|
153
|
-
#
|
|
154
|
-
#
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
try {
|
|
161
|
-
const cwd = process.env.CLAUDE_CWD || process.env.CURSOR_CWD || process.cwd();
|
|
162
|
-
const lpPath = path.join(cwd, '.muggle-ai', 'last-project.json');
|
|
163
|
-
if (!fs.existsSync(lpPath)) { console.log(''); return; }
|
|
164
|
-
const raw = JSON.parse(fs.readFileSync(lpPath, 'utf-8'));
|
|
165
|
-
const lp = raw && raw.lastProject;
|
|
166
|
-
if (!lp || !lp.projectId) { console.log(''); return; }
|
|
167
|
-
const safeName = String(lp.projectName || '').replace(/\"/g, '\\\\\"');
|
|
168
|
-
console.log('Muggle Test Last Project: id=' + lp.projectId + ' url=' + lp.projectUrl + ' name=\"' + safeName + '\"');
|
|
169
|
-
} catch { console.log(''); }
|
|
170
|
-
" 2>/dev/null || true)
|
|
171
|
-
if [ -n "$last_project_line" ]; then
|
|
172
|
-
last_project_note="\\n\\n${last_project_line}"
|
|
173
|
-
fi
|
|
174
|
-
|
|
175
|
-
# --- Last-host cache injection ---
|
|
176
|
-
# Per-repo cache of the local dev server URL the user picked on the previous
|
|
177
|
-
# run. Lives at <cwd>/.muggle-ai/last-host.json. Skills silently reuse it
|
|
178
|
-
# when the user has set the autoSelectLocalHost preference to "always".
|
|
179
|
-
last_host_line=""
|
|
180
|
-
last_host_note=""
|
|
181
|
-
last_host_line=$(node -e "
|
|
176
|
+
# --- Last-used cache injection ---
|
|
177
|
+
# The "last used Muggle Test project" and "last used local dev server URL"
|
|
178
|
+
# caches live in ~/.muggle-ai/, keyed by working directory, and are honored by
|
|
179
|
+
# skills when autoSelectProject / autoSelectLocalHost = always. A cache written
|
|
180
|
+
# before the move to the home directory still sits in <cwd>/.muggle-ai/ and is
|
|
181
|
+
# read as a fallback, so those sessions keep their context lines.
|
|
182
|
+
last_cache_notes=""
|
|
183
|
+
last_cache_notes=$(node -e "
|
|
182
184
|
const fs = require('fs');
|
|
185
|
+
const os = require('os');
|
|
183
186
|
const path = require('path');
|
|
184
187
|
try {
|
|
185
188
|
const cwd = process.env.CLAUDE_CWD || process.env.CURSOR_CWD || process.cwd();
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
|
|
189
|
+
const parseFile = (filePath) => {
|
|
190
|
+
try { return JSON.parse(fs.readFileSync(filePath, 'utf-8')); } catch { return null; }
|
|
191
|
+
};
|
|
192
|
+
const readEntry = (fileName, legacyEntryKey) => {
|
|
193
|
+
const home = parseFile(path.join(os.homedir(), '.muggle-ai', fileName));
|
|
194
|
+
const homeEntry = home && home.entries && home.entries[path.resolve(cwd)];
|
|
195
|
+
if (homeEntry) { return homeEntry; }
|
|
196
|
+
const legacy = parseFile(path.join(cwd, '.muggle-ai', fileName));
|
|
197
|
+
return (legacy && legacy[legacyEntryKey]) || null;
|
|
198
|
+
};
|
|
199
|
+
const lines = [];
|
|
200
|
+
const lastProject = readEntry('last-project.json', 'lastProject');
|
|
201
|
+
if (lastProject && lastProject.projectId) {
|
|
202
|
+
const safeName = String(lastProject.projectName || '').replace(/\"/g, '\\\\\"');
|
|
203
|
+
lines.push('Muggle Test Last Project: id=' + lastProject.projectId + ' url=' + lastProject.projectUrl + ' name=\"' + safeName + '\"');
|
|
204
|
+
}
|
|
205
|
+
const lastHost = readEntry('last-host.json', 'lastHost');
|
|
206
|
+
if (lastHost && lastHost.host) {
|
|
207
|
+
lines.push('Muggle Test Last Host: ' + lastHost.host);
|
|
208
|
+
}
|
|
209
|
+
console.log(lines.map((line) => '\\\\n\\\\n' + line).join(''));
|
|
192
210
|
} catch { console.log(''); }
|
|
193
211
|
" 2>/dev/null || true)
|
|
194
|
-
if [ -n "$last_host_line" ]; then
|
|
195
|
-
last_host_note="\\n\\n${last_host_line}"
|
|
196
|
-
fi
|
|
197
212
|
|
|
198
|
-
context="<EXTREMELY_IMPORTANT>\nYou have access to Muggle AI — a real-browser E2E acceptance testing tool.\n\nWhenever the user asks you to test, validate, verify, or check if their web app works — use the muggle MCP tools. This includes:\n- Testing user flows (signup, login, checkout, forms, dashboards)\n- Verifying UI changes didn't break anything\n- Running regression tests after code changes\n- Validating frontend behavior on localhost or a dev server\n- Checking if a feature works before merging a PR\n\nMuggle Test launches a real Electron browser that clicks buttons, fills forms, navigates pages, and captures screenshots. It generates replayable test scripts that persist across sessions.\n\nDo NOT write test code (Playwright, Cypress, Selenium) or try to test UI manually when muggle tools are available. Use the muggle skill or muggle MCP tools instead — they are faster, capture visual evidence, and produce reusable test scripts.\n\nTrigger phrases: 'test my app', 'check if it works', 'run E2E acceptance tests', 'validate the UI', 'verify the flow', 'regression test', 'make sure it still works', 'test before merging'.\n</EXTREMELY_IMPORTANT>${upgrade_notice}${prefs_file_note}${
|
|
213
|
+
context="<EXTREMELY_IMPORTANT>\nYou have access to Muggle AI — a real-browser E2E acceptance testing tool.\n\nWhenever the user asks you to test, validate, verify, or check if their web app works — use the muggle MCP tools. This includes:\n- Testing user flows (signup, login, checkout, forms, dashboards)\n- Verifying UI changes didn't break anything\n- Running regression tests after code changes\n- Validating frontend behavior on localhost or a dev server\n- Checking if a feature works before merging a PR\n\nMuggle Test launches a real Electron browser that clicks buttons, fills forms, navigates pages, and captures screenshots. It generates replayable test scripts that persist across sessions.\n\nDo NOT write test code (Playwright, Cypress, Selenium) or try to test UI manually when muggle tools are available. Use the muggle skill or muggle MCP tools instead — they are faster, capture visual evidence, and produce reusable test scripts.\n\nTrigger phrases: 'test my app', 'check if it works', 'run E2E acceptance tests', 'validate the UI', 'verify the flow', 'regression test', 'make sure it still works', 'test before merging'.\n</EXTREMELY_IMPORTANT>${upgrade_notice}${prefs_file_note}${last_cache_notes}"
|
|
199
214
|
|
|
200
215
|
escaped_context=$(escape_for_json "$context")
|
|
201
216
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -uo pipefail
|
|
3
|
+
|
|
4
|
+
# pre-execution-classification gate (PreToolUse/muggle local execution tools).
|
|
5
|
+
# Denies a generation or replay whose test case was never classified per
|
|
6
|
+
# muggle-test Step 6f. That step calls muggle-remote-test-script-list, which is
|
|
7
|
+
# where the run finds out the test case has never passed or has failed
|
|
8
|
+
# repeatedly — the check is free before dispatch and costs a full browser run
|
|
9
|
+
# once skipped.
|
|
10
|
+
#
|
|
11
|
+
# The pre-filter re-states the execution tool names the gate acts on, so an
|
|
12
|
+
# unrelated payload never pays Node cold-start. It stays cheap by construction:
|
|
13
|
+
# an execution call happens minutes apart and burns a real browser, so the spawn
|
|
14
|
+
# is noise against what it protects. Degrades to {}.
|
|
15
|
+
payload="$(cat)"
|
|
16
|
+
|
|
17
|
+
if ! grep -Eiq 'muggle-local-(execute-test-generation|execute-replay)' <<<"$payload"; then
|
|
18
|
+
printf '{}'
|
|
19
|
+
exit 0
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
root="${CLAUDE_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-}}"
|
|
23
|
+
printf '%s' "$payload" | node "${root}/scripts/guardrails.mjs" classify-gate 2>/dev/null || printf '{}'
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -uo pipefail
|
|
3
|
+
|
|
4
|
+
# debug-path gate (Stop). When a run failed this session and never went through
|
|
5
|
+
# _shared/debug-failed-run.md, block the turn end naming the run (or the
|
|
6
|
+
# MUGGLE_DEBUG_SKIP escape hatch). muggle-test Step 7C marks that routing
|
|
7
|
+
# mandatory and it was still routinely skipped, so failures were summarized and
|
|
8
|
+
# dropped — the run a reviewer most needs to see is the one nobody looked at.
|
|
9
|
+
#
|
|
10
|
+
# Mirrors guardrail-watch-gate.sh: synchronous (only a sync Stop hook can block
|
|
11
|
+
# the turn end), fires on EVERY turn end, and pre-filters in shell so Node spawns
|
|
12
|
+
# only when a failed run is recorded and unresolved. On the overwhelming majority
|
|
13
|
+
# of turns nothing failed, so the state file is absent or failedRuns is empty and
|
|
14
|
+
# we return {} in-shell. The evidence join runs in guardrails.mjs. Degrades to {}.
|
|
15
|
+
payload="$(cat)"
|
|
16
|
+
|
|
17
|
+
raw_sid="$(printf '%s' "$payload" | grep -oE '"session_id"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed -E 's/.*:[[:space:]]*"([^"]*)".*/\1/')"
|
|
18
|
+
[ -n "$raw_sid" ] || raw_sid="unknown"
|
|
19
|
+
sid="$(printf '%s' "$raw_sid" | sed 's/[^A-Za-z0-9_-]/_/g')"
|
|
20
|
+
|
|
21
|
+
# Resolve the same home dir Node's os.homedir() uses. HOME is correct on
|
|
22
|
+
# macOS/Linux and on most Git Bash setups; fall back to converting USERPROFILE
|
|
23
|
+
# when HOME doesn't hold the state dir (some Windows shells point HOME elsewhere).
|
|
24
|
+
home="${HOME:-}"
|
|
25
|
+
if [ ! -d "$home/.muggle-ai" ] && command -v cygpath >/dev/null 2>&1 && [ -n "${USERPROFILE:-}" ]; then
|
|
26
|
+
home="$(cygpath -u "$USERPROFILE" 2>/dev/null || printf '%s' "$home")"
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# Empty array serializes as `"failedRuns": []` (one line); a non-empty array
|
|
30
|
+
# spans lines, so the empty match reliably tells them apart.
|
|
31
|
+
state_file="$home/.muggle-ai/guardrails/$sid.json"
|
|
32
|
+
if [ ! -f "$state_file" ] \
|
|
33
|
+
|| ! grep -q '"failedRuns"' "$state_file" \
|
|
34
|
+
|| grep -q '"failedRuns": \[\]' "$state_file" \
|
|
35
|
+
|| grep -q '"debugSkipped": true' "$state_file"; then
|
|
36
|
+
printf '{}'
|
|
37
|
+
exit 0
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
root="${CLAUDE_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-}}"
|
|
41
|
+
printf '%s' "$payload" | node "${root}/scripts/guardrails.mjs" debug-path-gate 2>/dev/null || printf '{}'
|
|
@@ -9,10 +9,14 @@ set -uo pipefail
|
|
|
9
9
|
# AskUserQuestion offer runs. Decision logic lives in the bundled guardrails.mjs.
|
|
10
10
|
#
|
|
11
11
|
# Fires after every Bash call, so a keyword pre-filter for the terminal output
|
|
12
|
-
# shapes keeps Node off the hot path.
|
|
12
|
+
# shapes keeps Node off the hot path. The reopen line belongs here too: it is
|
|
13
|
+
# the one signal that retracts a terminal verdict, and while the pre-filter
|
|
14
|
+
# dropped it a close+reopen — routine, to re-fire a lost workflow trigger — left
|
|
15
|
+
# the handoff armed on a change that is open again. Degrades to {} so it never
|
|
16
|
+
# blocks.
|
|
13
17
|
payload="$(cat)"
|
|
14
18
|
|
|
15
|
-
if ! grep -Eiq 'merged
|
|
19
|
+
if ! grep -Eiq '(merged|closed|reopened) pull request|TERMINAL pr=' <<<"$payload"; then
|
|
16
20
|
printf '{}'
|
|
17
21
|
exit 0
|
|
18
22
|
fi
|