@muggleai/works 5.9.0 → 5.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/README.md +8 -0
  2. package/dist/{chunk-LVUJ4E62.js → chunk-MSPMXBO5.js} +638 -468
  3. package/dist/{chunk-5PYK7DWB.js → chunk-WUMAJOBT.js} +17 -6
  4. package/dist/cli.js +2 -2
  5. package/dist/index.js +2 -2
  6. package/dist/plugin/.claude-plugin/plugin.json +1 -1
  7. package/dist/plugin/.cursor-plugin/plugin.json +1 -1
  8. package/dist/plugin/README.md +19 -13
  9. package/dist/plugin/agents/test-prepare-runner.md +4 -1
  10. package/dist/plugin/agents/visual-walkthrough-builder.md +12 -5
  11. package/dist/plugin/commands/mtest.md +1 -1
  12. package/dist/plugin/hooks/README.md +15 -3
  13. package/dist/plugin/hooks/hooks.json +79 -0
  14. package/dist/plugin/scripts/ensure-electron-app.sh +62 -47
  15. package/dist/plugin/scripts/guardrail-classify-gate.sh +23 -0
  16. package/dist/plugin/scripts/guardrail-debug-path-gate.sh +41 -0
  17. package/dist/plugin/scripts/guardrail-pr-terminal.sh +6 -2
  18. package/dist/plugin/scripts/guardrail-record-stage-read.sh +21 -0
  19. package/dist/plugin/scripts/guardrail-record-stage-signals.sh +23 -0
  20. package/dist/plugin/scripts/guardrail-record-tests.sh +11 -4
  21. package/dist/plugin/scripts/guardrail-report-format.sh +9 -5
  22. package/dist/plugin/scripts/guardrail-skill-stages.sh +35 -0
  23. package/dist/plugin/scripts/guardrail-stage-gate.sh +43 -0
  24. package/dist/plugin/scripts/guardrail-walkthrough-gate.sh +41 -0
  25. package/dist/plugin/scripts/guardrails.mjs +474 -80
  26. package/dist/plugin/scripts/pr-watch-events.sh +116 -0
  27. package/dist/plugin/scripts/pr-watch-guards.sh +8 -2
  28. package/dist/plugin/scripts/pr-watch-loop.sh +254 -0
  29. package/dist/plugin/scripts/reconcile-stale-watchers.sh +66 -18
  30. package/dist/plugin/scripts/sign-body.sh +88 -0
  31. package/dist/plugin/skills/CLAUDE.md +16 -0
  32. package/dist/plugin/skills/_shared/post-merge-cleanup.md +83 -3
  33. package/dist/plugin/skills/_shared/pr-followup-helpers/loop-signature.md +6 -8
  34. package/dist/plugin/skills/_shared/pr-followup-helpers/reply-routing.md +10 -18
  35. package/dist/plugin/skills/_shared/resolve-e2e-validation-context.md +1 -0
  36. package/dist/plugin/skills/_shared/vcs/CLAUDE.md +1 -0
  37. package/dist/plugin/skills/_shared/vcs/github/pr-edit.md +3 -2
  38. package/dist/plugin/skills/_shared/vcs/github/reply-line-comment.md +3 -2
  39. package/dist/plugin/skills/_shared/vcs/github/top-level-comment.md +3 -2
  40. package/dist/plugin/skills/_shared/vcs/gitlab/mr-create.md +4 -1
  41. package/dist/plugin/skills/_shared/vcs/gitlab/mr-edit.md +4 -1
  42. package/dist/plugin/skills/_shared/vcs/gitlab/mr-note.md +4 -1
  43. package/dist/plugin/skills/_shared/vcs/gitlab/reply-discussion.md +4 -1
  44. package/dist/plugin/skills/_shared/vcs/post-signature.md +23 -12
  45. package/dist/plugin/skills/do/open-prs/forward.md +2 -2
  46. package/dist/plugin/skills/do/per-comment-replies.md +2 -15
  47. package/dist/plugin/skills/muggle/SKILL.md +1 -1
  48. package/dist/plugin/skills/muggle-browser-task/SKILL.md +1 -1
  49. package/dist/plugin/skills/muggle-pr-followup/CLAUDE.md +2 -1
  50. package/dist/plugin/skills/muggle-pr-followup/SKILL.md +6 -3
  51. package/dist/plugin/skills/muggle-pr-followup/adopt.md +60 -0
  52. package/dist/plugin/skills/muggle-pr-followup/arm-watcher.md +10 -14
  53. package/dist/plugin/skills/muggle-pr-followup/auto-track.md +2 -0
  54. package/dist/plugin/skills/muggle-pr-followup/evals/evals.json +63 -1
  55. package/dist/plugin/skills/muggle-pr-followup/output-templates/help.md +11 -1
  56. package/dist/plugin/skills/muggle-pr-followup/output-templates/inline-reply.md +1 -7
  57. package/dist/plugin/skills/muggle-pr-followup/output-templates/resolve-reminder.md +1 -4
  58. package/dist/plugin/skills/muggle-pr-followup/reconcile.md +32 -6
  59. package/dist/plugin/skills/muggle-pr-followup/state-schemas.md +18 -0
  60. package/dist/plugin/skills/muggle-preferences/SKILL.md +1 -1
  61. package/dist/plugin/skills/muggle-preferences/evals/evals.json +45 -0
  62. package/dist/plugin/skills/muggle-preferences/ops/change-one.md +1 -1
  63. package/dist/plugin/skills/muggle-preferences/ops/configure.md +4 -5
  64. package/dist/plugin/skills/muggle-preferences/ops/list.md +1 -1
  65. package/dist/plugin/skills/muggle-preferences/ops/set.md +3 -4
  66. package/dist/plugin/skills/muggle-preferences/preference-gates/README.md +3 -3
  67. package/dist/plugin/skills/muggle-preferences/preference-gates/autoSelectLocalHost.md +2 -2
  68. package/dist/plugin/skills/muggle-preferences/preference-gates/autoSelectProject.md +1 -1
  69. package/dist/plugin/skills/muggle-preferences/preference-gates/watcherLifetime.md +23 -0
  70. package/dist/plugin/skills/muggle-test/SKILL.md +23 -3
  71. package/dist/plugin/skills/muggle-test/execute-local.md +2 -0
  72. package/dist/plugin/skills/muggle-test-feature-local/SKILL.md +6 -1
  73. package/dist/plugin/skills/muggle-test-import/SKILL.md +1 -1
  74. package/dist/plugin/skills/muggle-test-prepare/SKILL.md +28 -15
  75. package/dist/plugin/skills/muggle-test-prepare/steps/check-running.md +1 -1
  76. package/dist/plugin/skills/muggle-test-prepare/steps/confirm-recipe.md +56 -0
  77. package/dist/plugin/skills/muggle-test-prepare/steps/derive-service-graph.md +51 -0
  78. package/dist/plugin/skills/muggle-test-prepare/steps/e2e-instructions.md +111 -0
  79. package/dist/plugin/skills/muggle-test-prepare/steps/identify-services.md +4 -2
  80. package/dist/plugin/skills/muggle-test-prepare/steps/readiness-report.md +12 -5
  81. package/dist/plugin/skills/muggle-test-prepare/steps/record-resolution.md +32 -0
  82. package/dist/plugin/skills/muggle-test-prepare/steps/replay-or-learn.md +20 -0
  83. package/dist/plugin/skills/muggle-test-prepare/steps/reuse-plan.md +9 -6
  84. package/dist/plugin/skills/muggle-test-prepare/steps/smoke-test.md +5 -1
  85. package/dist/plugin/skills/muggle-test-prepare/steps/start-services.md +3 -1
  86. package/dist/plugin/skills/muggle-test-regenerate-missing/SKILL.md +1 -1
  87. package/dist/release-manifest.json +4 -4
  88. package/dist/src-B5OTAZKM.js +1 -0
  89. package/package.json +7 -6
  90. package/plugin/.claude-plugin/plugin.json +1 -1
  91. package/plugin/.cursor-plugin/plugin.json +1 -1
  92. package/plugin/README.md +19 -13
  93. package/plugin/agents/test-prepare-runner.md +4 -1
  94. package/plugin/agents/visual-walkthrough-builder.md +12 -5
  95. package/plugin/commands/mtest.md +1 -1
  96. package/plugin/hooks/README.md +15 -3
  97. package/plugin/hooks/hooks.json +79 -0
  98. package/plugin/scripts/ensure-electron-app.sh +62 -47
  99. package/plugin/scripts/guardrail-classify-gate.sh +23 -0
  100. package/plugin/scripts/guardrail-debug-path-gate.sh +41 -0
  101. package/plugin/scripts/guardrail-pr-terminal.sh +6 -2
  102. package/plugin/scripts/guardrail-record-stage-read.sh +21 -0
  103. package/plugin/scripts/guardrail-record-stage-signals.sh +23 -0
  104. package/plugin/scripts/guardrail-record-tests.sh +11 -4
  105. package/plugin/scripts/guardrail-report-format.sh +9 -5
  106. package/plugin/scripts/guardrail-skill-stages.sh +35 -0
  107. package/plugin/scripts/guardrail-stage-gate.sh +43 -0
  108. package/plugin/scripts/guardrail-walkthrough-gate.sh +41 -0
  109. package/plugin/scripts/guardrails.mjs +474 -80
  110. package/plugin/scripts/pr-watch-events.sh +116 -0
  111. package/plugin/scripts/pr-watch-guards.sh +8 -2
  112. package/plugin/scripts/pr-watch-loop.sh +254 -0
  113. package/plugin/scripts/reconcile-stale-watchers.sh +66 -18
  114. package/plugin/scripts/sign-body.sh +88 -0
  115. package/plugin/skills/CLAUDE.md +16 -0
  116. package/plugin/skills/_shared/post-merge-cleanup.md +83 -3
  117. package/plugin/skills/_shared/pr-followup-helpers/loop-signature.md +6 -8
  118. package/plugin/skills/_shared/pr-followup-helpers/reply-routing.md +10 -18
  119. package/plugin/skills/_shared/resolve-e2e-validation-context.md +1 -0
  120. package/plugin/skills/_shared/vcs/CLAUDE.md +1 -0
  121. package/plugin/skills/_shared/vcs/github/pr-edit.md +3 -2
  122. package/plugin/skills/_shared/vcs/github/reply-line-comment.md +3 -2
  123. package/plugin/skills/_shared/vcs/github/top-level-comment.md +3 -2
  124. package/plugin/skills/_shared/vcs/gitlab/mr-create.md +4 -1
  125. package/plugin/skills/_shared/vcs/gitlab/mr-edit.md +4 -1
  126. package/plugin/skills/_shared/vcs/gitlab/mr-note.md +4 -1
  127. package/plugin/skills/_shared/vcs/gitlab/reply-discussion.md +4 -1
  128. package/plugin/skills/_shared/vcs/post-signature.md +23 -12
  129. package/plugin/skills/do/open-prs/forward.md +2 -2
  130. package/plugin/skills/do/per-comment-replies.md +2 -15
  131. package/plugin/skills/muggle/SKILL.md +1 -1
  132. package/plugin/skills/muggle-browser-task/SKILL.md +1 -1
  133. package/plugin/skills/muggle-pr-followup/CLAUDE.md +2 -1
  134. package/plugin/skills/muggle-pr-followup/SKILL.md +6 -3
  135. package/plugin/skills/muggle-pr-followup/adopt.md +60 -0
  136. package/plugin/skills/muggle-pr-followup/arm-watcher.md +10 -14
  137. package/plugin/skills/muggle-pr-followup/auto-track.md +2 -0
  138. package/plugin/skills/muggle-pr-followup/evals/evals.json +63 -1
  139. package/plugin/skills/muggle-pr-followup/output-templates/help.md +11 -1
  140. package/plugin/skills/muggle-pr-followup/output-templates/inline-reply.md +1 -7
  141. package/plugin/skills/muggle-pr-followup/output-templates/resolve-reminder.md +1 -4
  142. package/plugin/skills/muggle-pr-followup/reconcile.md +32 -6
  143. package/plugin/skills/muggle-pr-followup/state-schemas.md +18 -0
  144. package/plugin/skills/muggle-preferences/SKILL.md +1 -1
  145. package/plugin/skills/muggle-preferences/evals/evals.json +45 -0
  146. package/plugin/skills/muggle-preferences/ops/change-one.md +1 -1
  147. package/plugin/skills/muggle-preferences/ops/configure.md +4 -5
  148. package/plugin/skills/muggle-preferences/ops/list.md +1 -1
  149. package/plugin/skills/muggle-preferences/ops/set.md +3 -4
  150. package/plugin/skills/muggle-preferences/preference-gates/README.md +3 -3
  151. package/plugin/skills/muggle-preferences/preference-gates/autoSelectLocalHost.md +2 -2
  152. package/plugin/skills/muggle-preferences/preference-gates/autoSelectProject.md +1 -1
  153. package/plugin/skills/muggle-preferences/preference-gates/watcherLifetime.md +23 -0
  154. package/plugin/skills/muggle-test/SKILL.md +23 -3
  155. package/plugin/skills/muggle-test/execute-local.md +2 -0
  156. package/plugin/skills/muggle-test-feature-local/SKILL.md +6 -1
  157. package/plugin/skills/muggle-test-import/SKILL.md +1 -1
  158. package/plugin/skills/muggle-test-prepare/SKILL.md +28 -15
  159. package/plugin/skills/muggle-test-prepare/steps/check-running.md +1 -1
  160. package/plugin/skills/muggle-test-prepare/steps/confirm-recipe.md +56 -0
  161. package/plugin/skills/muggle-test-prepare/steps/derive-service-graph.md +51 -0
  162. package/plugin/skills/muggle-test-prepare/steps/e2e-instructions.md +111 -0
  163. package/plugin/skills/muggle-test-prepare/steps/identify-services.md +4 -2
  164. package/plugin/skills/muggle-test-prepare/steps/readiness-report.md +12 -5
  165. package/plugin/skills/muggle-test-prepare/steps/record-resolution.md +32 -0
  166. package/plugin/skills/muggle-test-prepare/steps/replay-or-learn.md +20 -0
  167. package/plugin/skills/muggle-test-prepare/steps/reuse-plan.md +9 -6
  168. package/plugin/skills/muggle-test-prepare/steps/smoke-test.md +5 -1
  169. package/plugin/skills/muggle-test-prepare/steps/start-services.md +3 -1
  170. package/plugin/skills/muggle-test-regenerate-missing/SKILL.md +1 -1
  171. package/dist/src-FDK2KQSZ.js +0 -1
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env bash
2
+ set -uo pipefail
3
+
4
+ # stage-signal observer (PostToolUse/Bash + muggle telemetry and feedback tools).
5
+ # Records the three things the stage gates settle from: a Step 6f
6
+ # pre-execution-classification emit, debug-path evidence for a failed run (a
7
+ # failure-classified emit or feedback naming it), and the skip markers each gate
8
+ # documents as its way out. Emits no directive — the gates read the state.
9
+ #
10
+ # The marker arm matches the MUGGLE_<GATE>_SKIP shape, never one token: a gate
11
+ # whose marker is missing from a hand-listed set instructs the user to run an
12
+ # echo that can never register, then blocks the turn anyway. Anchoring the
13
+ # marker to a leading `echo` stays in guardrails.mjs; over-matching here only
14
+ # costs a needless spawn. Degrades to {}.
15
+ payload="$(cat)"
16
+
17
+ if ! grep -Eiq 'MUGGLE_[A-Z0-9_]+_SKIP|muggle.*(telemetry-event-emit|user-feedback-create)' <<<"$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" record-stage-signals 2>/dev/null || printf '{}'
@@ -10,12 +10,19 @@ set -uo pipefail
10
10
  # pre-filter for test runners and the muggle E2E tool names keeps Node off the
11
11
  # hot path. Only a `test` command (npm/pnpm/yarn/jest/vitest/pytest/go/cargo),
12
12
  # a muggle execute/replay/test-generation event, a muggle-test skill telemetry
13
- # emit (registers a clean-SKIP verdict as an E2E run), or an E2E skip marker
14
- # reaches guardrails.mjs, which then inspects the output for pass/fail and
15
- # updates state. Degrades to {}.
13
+ # emit (registers a clean-SKIP verdict as an E2E run), a skip marker, or a PR
14
+ # publish carrying a rendered walkthrough reaches guardrails.mjs, which then
15
+ # inspects the output for pass/fail and updates state. Degrades to {}.
16
+ #
17
+ # The marker arm matches the MUGGLE_<GATE>_SKIP shape, never one token: every
18
+ # Stop gate documents its own marker as the escape hatch, and a per-token list
19
+ # left the watcher and walkthrough markers unreachable — the gates instructed
20
+ # the user to run an echo that could never reach the recorder, then blocked the
21
+ # turn anyway. Anchoring the marker to a leading `echo` stays in guardrails.mjs;
22
+ # over-matching here only costs a needless spawn.
16
23
  payload="$(cat)"
17
24
 
18
- if ! grep -Eiq '(pnpm|npm|yarn)[[:space:]]+(run[[:space:]]+)?test|jest|vitest|pytest|go[[:space:]]+test|cargo[[:space:]]+test|muggle.*(execute|test-generation|replay)|muggle-local-telemetry-skill-emit|MUGGLE_E2E_SKIP' <<<"$payload"; then
25
+ if ! grep -Eiq '(pnpm|npm|yarn)[[:space:]]+(run[[:space:]]+)?test|jest|vitest|pytest|go[[:space:]]+test|cargo[[:space:]]+test|muggle.*(execute|test-generation|replay)|muggle-local-telemetry-skill-emit|MUGGLE_[A-Z0-9_]+_SKIP|gh[[:space:]]+pr[[:space:]]+(comment|create|edit)|issues/comments/[0-9]' <<<"$payload"; then
19
26
  printf '{}'
20
27
  exit 0
21
28
  fi
@@ -7,14 +7,18 @@ set -uo pipefail
7
7
  # deterministic renderer.
8
8
  #
9
9
  # This must stay synchronous (only a sync PreToolUse hook can deny), and it fires
10
- # before every Bash call. A keyword pre-filter for the three PR-posting commands
10
+ # before every Bash call. A keyword pre-filter for the PR-publishing commands
11
11
  # keeps Node off the hot path: a plain `ls`/`git status`/build command returns {}
12
- # in-shell and never pays cold-start. Only a `gh pr comment|create|edit` reaches
13
- # guardrails.mjs, which reads the body (incl. --body-file) and decides. Degrades
14
- # to {} so it never blocks an unrelated command.
12
+ # in-shell and never pays cold-start. Only a `gh pr comment|create|edit` or a
13
+ # `gh api` comment edit reaches guardrails.mjs, which reads the body (incl.
14
+ # --body-file) and decides. The comment-edit arm matters as much as the post
15
+ # arm: guardrails.mjs gates that path precisely so a sanctioned walkthrough
16
+ # can't be overwritten with hand-written markdown afterwards, and a pre-filter
17
+ # on `gh pr` alone left that bypass wide open. Degrades to {} so it never blocks
18
+ # an unrelated command.
15
19
  payload="$(cat)"
16
20
 
17
- if ! grep -Eiq 'gh[[:space:]]+pr[[:space:]]+(comment|create|edit)' <<<"$payload"; then
21
+ if ! grep -Eiq 'gh[[:space:]]+pr[[:space:]]+(comment|create|edit)|issues/comments/[0-9]' <<<"$payload"; then
18
22
  printf '{}'
19
23
  exit 0
20
24
  fi
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env bash
2
+ set -uo pipefail
3
+
4
+ # mandatory-stage recorder (PostToolUse/Skill). Records which skill is running
5
+ # and, when its SKILL.md declares `mandatoryStages`, returns those files as
6
+ # required reading at the moment of use. The Stop gate
7
+ # (guardrail-stage-gate.sh) blocks the turn end while any of them is unread.
8
+ #
9
+ # Skill calls are rare, so the pre-filter is a file test rather than a keyword
10
+ # scan: the invoked name is pulled out of the payload and Node runs only when it
11
+ # resolves to a skill this plugin actually ships. Another plugin's skill, or an
12
+ # input with no skill name, returns {} in-shell. Every key the resolver reads is
13
+ # accepted here — a key matched in one and missed in the other makes the gate
14
+ # dead code on exactly the harness that names it that way. Degrades to {}.
15
+ payload="$(cat)"
16
+
17
+ root="${CLAUDE_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-}}"
18
+
19
+ for candidate in $(printf '%s' "$payload" \
20
+ | grep -oE '"(skill|skillName|name|command)"[[:space:]]*:[[:space:]]*"[^"]*"' \
21
+ | sed -E 's/.*:[[:space:]]*"([^"]*)".*/\1/'); do
22
+ # A namespaced invocation (`muggle:muggle-test`) and a slash command
23
+ # (`/muggle-test`) both name the same skill directory.
24
+ skill="${candidate##*:}"
25
+ skill="${skill#/}"
26
+ case "$skill" in
27
+ "" | *[!A-Za-z0-9._-]*) continue ;;
28
+ esac
29
+ if [ -f "$root/skills/$skill/SKILL.md" ]; then
30
+ printf '%s' "$payload" | node "${root}/scripts/guardrails.mjs" skill-stages 2>/dev/null || printf '{}'
31
+ exit 0
32
+ fi
33
+ done
34
+
35
+ printf '{}'
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env bash
2
+ set -uo pipefail
3
+
4
+ # mandatory-stage gate (Stop). When a skill invoked this session declared
5
+ # `mandatoryStages` in its SKILL.md and any of those files was never opened,
6
+ # block the turn end naming them (or the MUGGLE_STAGE_SKIP escape hatch). This
7
+ # is the root fix for a skill read as a single page: the SKILL.md links out to
8
+ # steps that are mandatory, nothing forced them open, and the steps were
9
+ # silently dropped.
10
+ #
11
+ # Mirrors guardrail-watch-gate.sh: synchronous (only a sync Stop hook can block
12
+ # the turn end), fires on EVERY turn end, and pre-filters in shell so Node spawns
13
+ # only when a stage is actually owed. On the overwhelming majority of turns no
14
+ # skill declared stages, so the state file is absent or mandatoryStages is empty
15
+ # and we return {} in-shell. The read-vs-declared comparison runs in
16
+ # guardrails.mjs. Degrades to {}.
17
+ payload="$(cat)"
18
+
19
+ raw_sid="$(printf '%s' "$payload" | grep -oE '"session_id"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed -E 's/.*:[[:space:]]*"([^"]*)".*/\1/')"
20
+ [ -n "$raw_sid" ] || raw_sid="unknown"
21
+ sid="$(printf '%s' "$raw_sid" | sed 's/[^A-Za-z0-9_-]/_/g')"
22
+
23
+ # Resolve the same home dir Node's os.homedir() uses. HOME is correct on
24
+ # macOS/Linux and on most Git Bash setups; fall back to converting USERPROFILE
25
+ # when HOME doesn't hold the state dir (some Windows shells point HOME elsewhere).
26
+ home="${HOME:-}"
27
+ if [ ! -d "$home/.muggle-ai" ] && command -v cygpath >/dev/null 2>&1 && [ -n "${USERPROFILE:-}" ]; then
28
+ home="$(cygpath -u "$USERPROFILE" 2>/dev/null || printf '%s' "$home")"
29
+ fi
30
+
31
+ # Empty array serializes as `"mandatoryStages": []` (one line); a non-empty array
32
+ # spans lines, so the empty match reliably tells them apart.
33
+ state_file="$home/.muggle-ai/guardrails/$sid.json"
34
+ if [ ! -f "$state_file" ] \
35
+ || ! grep -q '"mandatoryStages"' "$state_file" \
36
+ || grep -q '"mandatoryStages": \[\]' "$state_file" \
37
+ || grep -q '"stageSkipped": true' "$state_file"; then
38
+ printf '{}'
39
+ exit 0
40
+ fi
41
+
42
+ root="${CLAUDE_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-}}"
43
+ printf '%s' "$payload" | node "${root}/scripts/guardrails.mjs" stage-gate 2>/dev/null || printf '{}'
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env bash
2
+ set -uo pipefail
3
+
4
+ # acceptance-run → walkthrough gate (Stop). When an E2E acceptance run happened
5
+ # this session and no visual walkthrough has reached the PR, block the turn end
6
+ # until it does or a skip is declared. Fires once per session per PR.
7
+ #
8
+ # This must stay synchronous (only a sync Stop hook can block the turn end), and
9
+ # it fires on EVERY turn end. There is no command payload to key off, so the
10
+ # pre-filter reads the same per-session state file guardrails.mjs uses and only
11
+ # spawns Node when the gate could actually fire — i.e. an acceptance run is
12
+ # recorded and the walkthrough is neither posted nor skipped. On the
13
+ # overwhelming majority of turns (no E2E this session) the state file is absent
14
+ # or e2eRun is unset, so we return {} in-shell and never pay Node cold-start —
15
+ # which also means the gate's `gh` lookups only ever run on turns that could
16
+ # genuinely owe a walkthrough. Degrades to {}.
17
+ payload="$(cat)"
18
+
19
+ raw_sid="$(printf '%s' "$payload" | grep -oE '"session_id"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed -E 's/.*:[[:space:]]*"([^"]*)".*/\1/')"
20
+ [ -n "$raw_sid" ] || raw_sid="unknown"
21
+ sid="$(printf '%s' "$raw_sid" | sed 's/[^A-Za-z0-9_-]/_/g')"
22
+
23
+ # Resolve the same home dir Node's os.homedir() uses. HOME is correct on
24
+ # macOS/Linux and on most Git Bash setups; fall back to converting USERPROFILE
25
+ # when HOME doesn't hold the state dir (some Windows shells point HOME elsewhere).
26
+ home="${HOME:-}"
27
+ if [ ! -d "$home/.muggle-ai" ] && command -v cygpath >/dev/null 2>&1 && [ -n "${USERPROFILE:-}" ]; then
28
+ home="$(cygpath -u "$USERPROFILE" 2>/dev/null || printf '%s' "$home")"
29
+ fi
30
+
31
+ state_file="$home/.muggle-ai/guardrails/$sid.json"
32
+ if [ ! -f "$state_file" ] \
33
+ || ! grep -q '"e2eRun": true' "$state_file" \
34
+ || grep -q '"walkthroughPosted": true' "$state_file" \
35
+ || grep -q '"walkthroughSkipped": 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" walkthrough-gate 2>/dev/null || printf '{}'