@muggleai/works 4.11.4 → 4.12.1
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/dist/{chunk-E52757MK.js → chunk-2DVZ2LYO.js} +5 -0
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/dist/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/plugin/.cursor-plugin/plugin.json +1 -1
- package/dist/plugin/scripts/ensure-electron-app.sh +29 -4
- package/dist/plugin/skills/_shared/github-cli-recipes/line-comments-for-review.md +8 -0
- package/dist/plugin/skills/_shared/github-cli-recipes/loop-user-identity.md +9 -0
- package/dist/plugin/skills/_shared/github-cli-recipes/pr-edit.md +8 -0
- package/dist/plugin/skills/_shared/github-cli-recipes/pr-metadata.md +12 -0
- package/dist/plugin/skills/_shared/github-cli-recipes/push-to-branch.md +10 -0
- package/dist/plugin/skills/_shared/github-cli-recipes/reply-line-comment.md +10 -0
- package/dist/plugin/skills/_shared/github-cli-recipes/submitted-reviews.md +15 -0
- package/dist/plugin/skills/_shared/github-cli-recipes/top-level-comment.md +7 -0
- package/dist/plugin/skills/_shared/github-cli-recipes/unresolved-threads.md +33 -0
- package/dist/plugin/skills/_shared/github-cli-recipes/verify-working-tree.md +17 -0
- package/dist/plugin/skills/_shared/github-cli-recipes.md +20 -0
- package/dist/plugin/skills/_shared/pr-followup-helpers/allow-list.md +55 -0
- package/dist/plugin/skills/_shared/pr-followup-helpers/classify.md +63 -0
- package/dist/plugin/skills/_shared/pr-followup-helpers/reply-routing.md +40 -0
- package/dist/plugin/skills/_shared/pr-followup-helpers.md +8 -146
- package/dist/plugin/skills/_shared/telemetry-events/muggle-do-cycle.md +26 -0
- package/dist/plugin/skills/_shared/telemetry-events/muggle-do-escalation.md +19 -0
- package/dist/plugin/skills/_shared/telemetry-events/muggle-do-resolve-reminder.md +19 -0
- package/dist/plugin/skills/_shared/telemetry-events/pr-followup-bootstrap.md +20 -0
- package/dist/plugin/skills/_shared/telemetry-events/pr-followup-tick.md +23 -0
- package/dist/plugin/skills/_shared/telemetry-events.md +15 -0
- package/dist/plugin/skills/do/address-reviews.md +137 -0
- package/dist/plugin/skills/do/build.md +5 -5
- package/dist/plugin/skills/do/open-prs/forward.md +80 -0
- package/dist/plugin/skills/do/open-prs/update.md +54 -0
- package/dist/plugin/skills/do/open-prs.md +6 -102
- package/dist/plugin/skills/do/per-comment-replies.md +61 -0
- package/dist/plugin/skills/do/resolve-reminder.md +70 -0
- package/dist/plugin/skills/muggle-do/SKILL.md +41 -45
- package/dist/plugin/skills/muggle-pr-followup/CLAUDE.md +25 -0
- package/dist/plugin/skills/muggle-pr-followup/SKILL.md +21 -43
- package/dist/plugin/skills/muggle-pr-followup/bootstrap.md +88 -0
- package/dist/plugin/skills/muggle-pr-followup/contract.md +44 -161
- package/dist/plugin/skills/muggle-pr-followup/output-templates/bootstrap.md +42 -0
- package/dist/plugin/skills/muggle-pr-followup/output-templates/escalation.md +32 -0
- package/dist/plugin/skills/muggle-pr-followup/output-templates/help.md +17 -0
- package/dist/plugin/skills/muggle-pr-followup/output-templates/inline-reply.md +17 -0
- package/dist/plugin/skills/muggle-pr-followup/output-templates/resolve-reminder.md +12 -0
- package/dist/plugin/skills/muggle-pr-followup/output-templates/watcher-log.md +21 -0
- package/dist/plugin/skills/muggle-pr-followup/output-templates.md +14 -0
- package/dist/plugin/skills/muggle-pr-followup/state-schemas.md +104 -0
- package/dist/plugin/skills/muggle-test-regenerate-missing/SKILL.md +3 -3
- package/dist/release-manifest.json +4 -4
- package/package.json +106 -106
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.cursor-plugin/plugin.json +1 -1
- package/plugin/scripts/ensure-electron-app.sh +29 -4
- package/plugin/skills/_shared/github-cli-recipes/line-comments-for-review.md +8 -0
- package/plugin/skills/_shared/github-cli-recipes/loop-user-identity.md +9 -0
- package/plugin/skills/_shared/github-cli-recipes/pr-edit.md +8 -0
- package/plugin/skills/_shared/github-cli-recipes/pr-metadata.md +12 -0
- package/plugin/skills/_shared/github-cli-recipes/push-to-branch.md +10 -0
- package/plugin/skills/_shared/github-cli-recipes/reply-line-comment.md +10 -0
- package/plugin/skills/_shared/github-cli-recipes/submitted-reviews.md +15 -0
- package/plugin/skills/_shared/github-cli-recipes/top-level-comment.md +7 -0
- package/plugin/skills/_shared/github-cli-recipes/unresolved-threads.md +33 -0
- package/plugin/skills/_shared/github-cli-recipes/verify-working-tree.md +17 -0
- package/plugin/skills/_shared/github-cli-recipes.md +20 -0
- package/plugin/skills/_shared/pr-followup-helpers/allow-list.md +55 -0
- package/plugin/skills/_shared/pr-followup-helpers/classify.md +63 -0
- package/plugin/skills/_shared/pr-followup-helpers/reply-routing.md +40 -0
- package/plugin/skills/_shared/pr-followup-helpers.md +8 -146
- package/plugin/skills/_shared/telemetry-events/muggle-do-cycle.md +26 -0
- package/plugin/skills/_shared/telemetry-events/muggle-do-escalation.md +19 -0
- package/plugin/skills/_shared/telemetry-events/muggle-do-resolve-reminder.md +19 -0
- package/plugin/skills/_shared/telemetry-events/pr-followup-bootstrap.md +20 -0
- package/plugin/skills/_shared/telemetry-events/pr-followup-tick.md +23 -0
- package/plugin/skills/_shared/telemetry-events.md +15 -0
- package/plugin/skills/do/address-reviews.md +137 -0
- package/plugin/skills/do/build.md +5 -5
- package/plugin/skills/do/open-prs/forward.md +80 -0
- package/plugin/skills/do/open-prs/update.md +54 -0
- package/plugin/skills/do/open-prs.md +6 -102
- package/plugin/skills/do/per-comment-replies.md +61 -0
- package/plugin/skills/do/resolve-reminder.md +70 -0
- package/plugin/skills/muggle-do/SKILL.md +41 -45
- package/plugin/skills/muggle-pr-followup/CLAUDE.md +25 -0
- package/plugin/skills/muggle-pr-followup/SKILL.md +21 -43
- package/plugin/skills/muggle-pr-followup/bootstrap.md +88 -0
- package/plugin/skills/muggle-pr-followup/contract.md +44 -161
- package/plugin/skills/muggle-pr-followup/output-templates/bootstrap.md +42 -0
- package/plugin/skills/muggle-pr-followup/output-templates/escalation.md +32 -0
- package/plugin/skills/muggle-pr-followup/output-templates/help.md +17 -0
- package/plugin/skills/muggle-pr-followup/output-templates/inline-reply.md +17 -0
- package/plugin/skills/muggle-pr-followup/output-templates/resolve-reminder.md +12 -0
- package/plugin/skills/muggle-pr-followup/output-templates/watcher-log.md +21 -0
- package/plugin/skills/muggle-pr-followup/output-templates.md +14 -0
- package/plugin/skills/muggle-pr-followup/state-schemas.md +104 -0
- package/plugin/skills/muggle-test-regenerate-missing/SKILL.md +3 -3
- package/scripts/postinstall.mjs +36 -5
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Escalation messages
|
|
2
|
+
|
|
3
|
+
Emitted by `/muggle-do` in address-reviews mode. One per invocation, at most. Anchored here so wording stays consistent.
|
|
4
|
+
|
|
5
|
+
## Ambiguous escalation
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
**Review-followup escalation — <owner>/<repo>#<n>**
|
|
9
|
+
|
|
10
|
+
I can't act on <count> review(s) without your input. Listed below; reply on GitHub by submitting a new review with clearer direction, or tell me here which way to go.
|
|
11
|
+
|
|
12
|
+
Review #<id> from <login>:
|
|
13
|
+
> <body or "(no body)">
|
|
14
|
+
|
|
15
|
+
Comments:
|
|
16
|
+
- <file>:<line> — <body>
|
|
17
|
+
- ...
|
|
18
|
+
|
|
19
|
+
[Repeat per ambiguous review in the batch.]
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Design-adjustment escalation
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
**Design-adjustment needed — <owner>/<repo>#<n>**
|
|
26
|
+
|
|
27
|
+
While addressing review #<id>, the cycle surfaced a conflict with the current design:
|
|
28
|
+
|
|
29
|
+
<one-paragraph description of the conflict>
|
|
30
|
+
|
|
31
|
+
This is beyond a routine code change. Confirm the design intent (reply here, or update the requirements doc) before I retry.
|
|
32
|
+
```
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Help output
|
|
2
|
+
|
|
3
|
+
For `/muggle:muggle-pr-followup` with no args, or with `help` / `?`:
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
muggle-pr-followup — watcher loop for PR review follow-ups
|
|
7
|
+
|
|
8
|
+
Active loops:
|
|
9
|
+
<slug> → <owner>/<repo>#<n> (cursor @ review #<id>, <N> cycles)
|
|
10
|
+
...
|
|
11
|
+
(or "no active loops")
|
|
12
|
+
|
|
13
|
+
Usage:
|
|
14
|
+
/muggle:muggle-pr-followup <pr-url> → bootstrap a new loop
|
|
15
|
+
/muggle:muggle-pr-followup <slug> <pr-number> → run one tick (called by /loop)
|
|
16
|
+
/muggle:muggle-pr-followup <pr-number> → run one tick (slug inferred from on-disk state)
|
|
17
|
+
```
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Per-comment inline reply
|
|
2
|
+
|
|
3
|
+
Posted via `gh api .../comments/<comment-id>/replies` per cycle, one per line comment.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
Addressed in <short-sha>: <one-line summary of the change made for THIS comment>.
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
`<short-sha>` is the first 7 chars of the new SHA. The body must contain that substring — the resolve-reminder stage greps for it to classify threads as addressed-by-loop.
|
|
10
|
+
|
|
11
|
+
## Top-level fallback (review-body-only)
|
|
12
|
+
|
|
13
|
+
When an actionable review has a non-empty body but zero line comments, GitHub has no `/replies` endpoint for the body. Post a top-level PR comment instead, at most once per such review:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
Re: review #<review_id> — addressed in <short-sha>: <one-line summary>.
|
|
17
|
+
```
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Resolve-reminder top-level PR comment
|
|
2
|
+
|
|
3
|
+
Posted via `gh pr comment` after the resolve-reminder stage scans threads. Only when at least one addressed-by-loop thread exists.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
I addressed these threads in <short-sha> — mark them resolved when satisfied:
|
|
7
|
+
- #<thread-id-1>
|
|
8
|
+
- #<thread-id-2>
|
|
9
|
+
- ...
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
If `addressed_by_loop == 0`, no comment is posted (silent).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Watcher tick log lines
|
|
2
|
+
|
|
3
|
+
The watcher does **not** print to the user during normal operation. It only appends to `followup.log`. All user-facing escalations come from `/muggle-do`.
|
|
4
|
+
|
|
5
|
+
## Idle tick
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
<ISO-8601> tick pr=<n> reviews_seen=0 idle
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Dispatching tick
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
<ISO-8601> tick pr=<n> reviews_seen=<count> dispatched=<id1>,<id2>,...
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Terminal tick
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
<ISO-8601> tick pr=<n> terminal=<merged|closed> result.md written
|
|
21
|
+
```
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Output templates
|
|
2
|
+
|
|
3
|
+
All user-facing message text used by the watcher, bootstrap, and `/muggle-do` in address-reviews mode. Each template group is its own file — load only what the current step emits.
|
|
4
|
+
|
|
5
|
+
## Index
|
|
6
|
+
|
|
7
|
+
| Group | Use case |
|
|
8
|
+
| :---- | :------- |
|
|
9
|
+
| [`bootstrap`](output-templates/bootstrap.md) | Bootstrap success summary + all bootstrap aborts. |
|
|
10
|
+
| [`watcher-log`](output-templates/watcher-log.md) | The three `followup.log` line shapes (idle, dispatching, terminal). |
|
|
11
|
+
| [`escalation`](output-templates/escalation.md) | `/muggle-do` terminal escalation messages (ambiguous, design-adjustment). |
|
|
12
|
+
| [`inline-reply`](output-templates/inline-reply.md) | Per-comment inline reply + top-level fallback for body-only reviews. |
|
|
13
|
+
| [`resolve-reminder`](output-templates/resolve-reminder.md) | Top-level PR comment for the resolve-reminder stage. |
|
|
14
|
+
| [`help`](output-templates/help.md) | Skill help output (no args / `help` / `?`). |
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Session State Schemas
|
|
2
|
+
|
|
3
|
+
Canonical shapes for the JSON files in a PR-follow-up session slot. The slot path is `.muggle-do/sessions/<slug>/` (the caller's session dir; `muggle-do` is the current and only caller).
|
|
4
|
+
|
|
5
|
+
All files are atomic writes — the caller rewrites the whole file each time, never mutates in place. Use a temp file + rename if the platform supports it.
|
|
6
|
+
|
|
7
|
+
## `prs.json`
|
|
8
|
+
|
|
9
|
+
A list of one entry. (Historical: the file is an array for forward-compat with the original session-wide model. Today, each PR has its own session slot, so the array always has exactly one entry.)
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
[
|
|
13
|
+
{
|
|
14
|
+
"repo": "<owner>/<repo>",
|
|
15
|
+
"number": <int>,
|
|
16
|
+
"url": "https://github.com/<owner>/<repo>/pull/<number>",
|
|
17
|
+
"head_sha": "<40-char-hex-sha>",
|
|
18
|
+
"state": "open" | "merged" | "closed"
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
- `state` is the **observed** state from the last `gh pr view`. The watcher refreshes it each tick.
|
|
24
|
+
- Terminal states (`merged`, `closed`) are sticky — once set, the watcher writes `result.md` and exits without rescheduling.
|
|
25
|
+
|
|
26
|
+
## `last_seen.json`
|
|
27
|
+
|
|
28
|
+
Keyed by `"<owner>/<repo>#<n>"`. One key per PR in the slot.
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"<owner>/<repo>#<n>": {
|
|
33
|
+
"reviewId": <int>,
|
|
34
|
+
"last_pushed_sha": "<sha-or-null>",
|
|
35
|
+
"idle_tick_count": <int>,
|
|
36
|
+
"cycles_completed": <int>,
|
|
37
|
+
"escalated_review_ids": [<int>, ...],
|
|
38
|
+
"pushed_shas": ["<sha>", ...]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
- `reviewId`: the cursor. The watcher fetches reviews with `id > reviewId`. Bootstrap pins this to the highest existing submitted review id (or `0` if none).
|
|
44
|
+
- `last_pushed_sha`: most recent SHA `/muggle-do` pushed in this PR's life; `null` until the first push.
|
|
45
|
+
- `idle_tick_count`: incremented each tick that sees zero new reviews. Reset to 0 on any tick that dispatches `/muggle-do`. Diagnostic only — does not gate behavior.
|
|
46
|
+
- `cycles_completed`: incremented each time `/muggle-do` completes an address-reviews invocation (regardless of actionable/ambiguous/mixed).
|
|
47
|
+
- `escalated_review_ids`: review ids classified as ambiguous by `/muggle-do`. The watcher excludes these from future review fetches so the same ambiguous review is never re-dispatched.
|
|
48
|
+
- `pushed_shas`: every SHA `/muggle-do` has pushed for this PR. Append-only. Used by the resolve-reminder stage to recognize threads addressed by the loop.
|
|
49
|
+
|
|
50
|
+
## `state.md`
|
|
51
|
+
|
|
52
|
+
Free-form markdown. No required schema beyond a few well-known fields the caller-agnostic code reads:
|
|
53
|
+
|
|
54
|
+
```markdown
|
|
55
|
+
# Session — <slug>
|
|
56
|
+
|
|
57
|
+
**PR:** <url>
|
|
58
|
+
**Slug:** <slug>
|
|
59
|
+
**Loop user:** <github-login> ← cached from `gh api user`
|
|
60
|
+
**Created:** <ISO-8601>
|
|
61
|
+
**Bootstrapped from URL:** <yes | no>
|
|
62
|
+
|
|
63
|
+
...free-form notes added by /muggle-do and bootstrap...
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The watcher does **not** read or write `state.md`. Only bootstrap, `/muggle-do`, and the caller's stages touch it.
|
|
67
|
+
|
|
68
|
+
## `followup.log`
|
|
69
|
+
|
|
70
|
+
Append-only line-per-tick log. One line per watcher tick, plus one line per `/muggle-do` invocation. Format is loose, but each line starts with an ISO-8601 timestamp:
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
2026-05-20T12:34:56Z tick pr=154 reviews_seen=0 idle
|
|
74
|
+
2026-05-20T12:35:56Z tick pr=154 reviews_seen=1 dispatched=4295962800
|
|
75
|
+
2026-05-20T12:36:14Z muggle-do cycle review_ids=[4295962800] outcome=pushed head_sha=abc1234
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Used for forensics only — never read back by skills.
|
|
79
|
+
|
|
80
|
+
## `result.md`
|
|
81
|
+
|
|
82
|
+
Written exactly once when the PR's watcher exits terminally (PR merged or closed). Free-form markdown summarizing the PR's life under this loop:
|
|
83
|
+
|
|
84
|
+
```markdown
|
|
85
|
+
# Result — <slug>
|
|
86
|
+
|
|
87
|
+
**PR:** <url>
|
|
88
|
+
**Final state:** merged | closed
|
|
89
|
+
**Cycles completed:** <int>
|
|
90
|
+
**Pushed SHAs:** <comma-separated list>
|
|
91
|
+
**Escalated review ids:** <comma-separated list or "none">
|
|
92
|
+
|
|
93
|
+
## Timeline
|
|
94
|
+
|
|
95
|
+
- <ISO-8601> bootstrap (cursor pinned at <reviewId>)
|
|
96
|
+
- <ISO-8601> review <id> from <login> — actionable, pushed <sha>
|
|
97
|
+
- <ISO-8601> review <id> from <login> — ambiguous, escalated
|
|
98
|
+
- ...
|
|
99
|
+
- <ISO-8601> PR <merged|closed> — watcher terminal
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Not in the slot
|
|
103
|
+
|
|
104
|
+
`cycle.json` and `requirements.md` are not seeded or read. `/muggle-do` reads reviews off GitHub each invocation.
|
|
@@ -180,15 +180,15 @@ Bulk regen does not run replays, so the section B (replay) router does not apply
|
|
|
180
180
|
|
|
181
181
|
### Step 8 — Open the Dashboard
|
|
182
182
|
|
|
183
|
-
Open the Muggle AI dashboard so the user can watch progress visually:
|
|
183
|
+
Open the Muggle AI dashboard so the user can watch progress visually. Point them at the **scripts** page (where DRAFT → GENERATING → ACTIVE transitions are visible), not `/runs` (which is for replay batches and shows nothing for a regen):
|
|
184
184
|
|
|
185
185
|
```bash
|
|
186
|
-
open "https://www.muggle-ai.com/muggleTestV0/dashboard/projects/{projectId}/
|
|
186
|
+
open "https://www.muggle-ai.com/muggleTestV0/dashboard/projects/{projectId}/scripts"
|
|
187
187
|
```
|
|
188
188
|
|
|
189
189
|
Tell them:
|
|
190
190
|
|
|
191
|
-
> "I've opened the project's
|
|
191
|
+
> "I've opened the project's scripts page. Generation jobs typically take a few minutes each — your test cases will move from `DRAFT` through `GENERATING` to `ACTIVE` as scripts complete."
|
|
192
192
|
|
|
193
193
|
### Step 9 (optional) — Poll Status
|
|
194
194
|
|
package/scripts/postinstall.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
/* global AbortController */
|
|
2
3
|
/**
|
|
3
4
|
* Postinstall script for @muggleai/works.
|
|
4
5
|
* Downloads the Electron app binary for local testing.
|
|
@@ -30,6 +31,8 @@ const VERSION_DIRECTORY_NAME_PATTERN = /^\d+\.\d+\.\d+(?:[-+][A-Za-z0-9.-]+)?$/;
|
|
|
30
31
|
const INSTALL_METADATA_FILE_NAME = ".install-metadata.json";
|
|
31
32
|
const INSTALL_MANIFEST_FILE_NAME = "install-manifest.json";
|
|
32
33
|
const LOG_FILE_NAME = "postinstall.log";
|
|
34
|
+
const FETCH_TIMEOUT_MS = 90_000;
|
|
35
|
+
const STREAM_TIMEOUT_MS = 5 * 60_000;
|
|
33
36
|
const VERSION_OVERRIDE_FILE_NAME = "electron-app-version-override.json";
|
|
34
37
|
const CURSOR_SKILLS_DIRECTORY_NAME = ".cursor";
|
|
35
38
|
const CURSOR_SKILLS_SUBDIRECTORY_NAME = "skills";
|
|
@@ -625,9 +628,22 @@ async function downloadElectronApp() {
|
|
|
625
628
|
// Create directories
|
|
626
629
|
mkdirSync(versionDir, { recursive: true });
|
|
627
630
|
|
|
628
|
-
// Download using fetch
|
|
631
|
+
// Download using fetch. Both fetch and pipeline get hard timeouts so a
|
|
632
|
+
// hung CDN/proxy can never pin this process — see issue #167.
|
|
629
633
|
log("Fetching...");
|
|
630
|
-
const
|
|
634
|
+
const fetchController = new AbortController();
|
|
635
|
+
const fetchTimer = setTimeout(() => fetchController.abort(), FETCH_TIMEOUT_MS);
|
|
636
|
+
let response;
|
|
637
|
+
try {
|
|
638
|
+
response = await fetch(downloadUrl, { signal: fetchController.signal });
|
|
639
|
+
} catch (error) {
|
|
640
|
+
if (error && error.name === "AbortError") {
|
|
641
|
+
throw new Error(`Fetch timed out after ${FETCH_TIMEOUT_MS}ms — ${downloadUrl}`, { cause: error });
|
|
642
|
+
}
|
|
643
|
+
throw error;
|
|
644
|
+
} finally {
|
|
645
|
+
clearTimeout(fetchTimer);
|
|
646
|
+
}
|
|
631
647
|
if (!response.ok) {
|
|
632
648
|
const errorBody = await response.text().catch(() => "");
|
|
633
649
|
throw new Error(
|
|
@@ -642,7 +658,18 @@ async function downloadElectronApp() {
|
|
|
642
658
|
|
|
643
659
|
const tempFile = join(versionDir, binaryName);
|
|
644
660
|
const fileStream = createWriteStream(tempFile);
|
|
645
|
-
|
|
661
|
+
const streamController = new AbortController();
|
|
662
|
+
const streamTimer = setTimeout(() => streamController.abort(), STREAM_TIMEOUT_MS);
|
|
663
|
+
try {
|
|
664
|
+
await pipeline(response.body, fileStream, { signal: streamController.signal });
|
|
665
|
+
} catch (error) {
|
|
666
|
+
if (error && error.name === "AbortError") {
|
|
667
|
+
throw new Error(`Download stream stalled — aborted after ${STREAM_TIMEOUT_MS}ms`, { cause: error });
|
|
668
|
+
}
|
|
669
|
+
throw error;
|
|
670
|
+
} finally {
|
|
671
|
+
clearTimeout(streamTimer);
|
|
672
|
+
}
|
|
646
673
|
|
|
647
674
|
log("Download complete, verifying checksum...");
|
|
648
675
|
|
|
@@ -895,10 +922,14 @@ function syncClaudePluginCache() {
|
|
|
895
922
|
}
|
|
896
923
|
}
|
|
897
924
|
|
|
898
|
-
// Run postinstall
|
|
925
|
+
// Run postinstall. The explicit `process.exit` guarantees the script doesn't
|
|
926
|
+
// linger when something below (a hung socket, an EPERM rmSync retry on
|
|
927
|
+
// Windows) keeps the event loop alive — see issue #167.
|
|
899
928
|
initLogFile();
|
|
900
929
|
removeVersionOverrideFile();
|
|
901
930
|
syncCursorSkills();
|
|
902
931
|
syncClaudePluginCache();
|
|
903
932
|
upsertCursorMcpConfig();
|
|
904
|
-
downloadElectronApp()
|
|
933
|
+
downloadElectronApp()
|
|
934
|
+
.catch(logError)
|
|
935
|
+
.finally(() => process.exit(0));
|