@snipcodeit/mgw 0.2.2 → 0.4.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.
@@ -1,9 +1,10 @@
1
1
  'use strict';
2
2
 
3
- var require$$0 = require('fs');
3
+ var require$$2 = require('fs');
4
4
  var require$$1 = require('path');
5
- var require$$0$1 = require('child_process');
6
- var require$$0$2 = require('events');
5
+ var require$$0 = require('child_process');
6
+ var require$$3 = require('os');
7
+ var require$$0$1 = require('events');
7
8
 
8
9
  function getDefaultExportFromCjs(x) {
9
10
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
@@ -15,7 +16,7 @@ var hasRequiredState;
15
16
  function requireState () {
16
17
  if (hasRequiredState) return state;
17
18
  hasRequiredState = 1;
18
- const fs = require$$0;
19
+ const fs = require$$2;
19
20
  const path = require$$1;
20
21
  function getMgwDir() {
21
22
  return path.join(process.cwd(), ".mgw");
@@ -84,17 +85,21 @@ function requireState () {
84
85
  return existing;
85
86
  }
86
87
  function migrateProjectState() {
88
+ const warnings = [];
87
89
  const existing = loadProjectState();
88
- if (!existing) return null;
90
+ if (!existing) return { state: null, warnings: [] };
89
91
  let changed = false;
90
92
  if (!existing.hasOwnProperty("active_gsd_milestone")) {
91
93
  existing.active_gsd_milestone = null;
92
94
  changed = true;
95
+ warnings.push("migration: added active_gsd_milestone field");
93
96
  }
94
97
  for (const m of existing.milestones || []) {
98
+ const mLabel = m.title || m.gsd_milestone_id || "unnamed";
95
99
  if (!m.hasOwnProperty("gsd_milestone_id")) {
96
100
  m.gsd_milestone_id = null;
97
101
  changed = true;
102
+ warnings.push(`migration: added gsd_milestone_id to milestone "${mLabel}"`);
98
103
  }
99
104
  if (!m.hasOwnProperty("gsd_state")) {
100
105
  m.gsd_state = null;
@@ -113,8 +118,9 @@ function requireState () {
113
118
  let entries;
114
119
  try {
115
120
  entries = fs.readdirSync(activeDir);
116
- } catch {
121
+ } catch (err) {
117
122
  entries = [];
123
+ warnings.push(`migration: could not read active dir: ${err.message}`);
118
124
  }
119
125
  for (const file of entries) {
120
126
  if (!file.endsWith(".json")) continue;
@@ -122,7 +128,8 @@ function requireState () {
122
128
  let issueState;
123
129
  try {
124
130
  issueState = JSON.parse(fs.readFileSync(filePath, "utf-8"));
125
- } catch {
131
+ } catch (err) {
132
+ warnings.push(`migration: skipping unreadable ${file}: ${err.message}`);
126
133
  continue;
127
134
  }
128
135
  let issueChanged = false;
@@ -137,12 +144,13 @@ function requireState () {
137
144
  if (issueChanged) {
138
145
  try {
139
146
  fs.writeFileSync(filePath, JSON.stringify(issueState, null, 2), "utf-8");
140
- } catch {
147
+ } catch (err) {
148
+ warnings.push(`migration: failed to write ${file}: ${err.message}`);
141
149
  }
142
150
  }
143
151
  }
144
152
  }
145
- return existing;
153
+ return { state: existing, warnings };
146
154
  }
147
155
  function resolveActiveMilestoneIndex(state) {
148
156
  if (!state) return -1;
@@ -157,6 +165,159 @@ function requireState () {
157
165
  }
158
166
  return -1;
159
167
  }
168
+ const VALID_LINK_TYPES = /* @__PURE__ */ new Set(["related", "implements", "tracks", "maps-to", "blocked-by"]);
169
+ function loadCrossRefs() {
170
+ const filePath = path.join(getMgwDir(), "cross-refs.json");
171
+ const warnings = [];
172
+ if (!fs.existsSync(filePath)) {
173
+ return { links: [], warnings: [] };
174
+ }
175
+ let raw;
176
+ try {
177
+ raw = JSON.parse(fs.readFileSync(filePath, "utf-8"));
178
+ } catch (err) {
179
+ return { links: [], warnings: [`cross-refs.json parse error: ${err.message}`] };
180
+ }
181
+ if (!raw || !Array.isArray(raw.links)) {
182
+ return { links: [], warnings: ["cross-refs.json missing links array"] };
183
+ }
184
+ const validLinks = [];
185
+ for (let i = 0; i < raw.links.length; i++) {
186
+ const link = raw.links[i];
187
+ const issues = [];
188
+ if (!link || typeof link !== "object") {
189
+ warnings.push(`cross-refs link[${i}]: not an object, skipping`);
190
+ continue;
191
+ }
192
+ if (typeof link.a !== "string" || !link.a) {
193
+ issues.push('missing "a"');
194
+ }
195
+ if (typeof link.b !== "string" || !link.b) {
196
+ issues.push('missing "b"');
197
+ }
198
+ if (link.type && !VALID_LINK_TYPES.has(link.type)) {
199
+ issues.push(`unknown type "${link.type}"`);
200
+ }
201
+ if (issues.length > 0) {
202
+ warnings.push(`cross-refs link[${i}]: ${issues.join(", ")}, skipping`);
203
+ continue;
204
+ }
205
+ validLinks.push(link);
206
+ }
207
+ return { links: validLinks, warnings };
208
+ }
209
+ function parseDependencies(body) {
210
+ if (!body || typeof body !== "string") return [];
211
+ const deps = /* @__PURE__ */ new Set();
212
+ const linePattern = /(?:depends[\s-]*on|blocked[\s-]*by)[:\s]+([^\n]+)/gi;
213
+ let match;
214
+ while ((match = linePattern.exec(body)) !== null) {
215
+ const refs = match[1];
216
+ const numPattern = /#(\d+)/g;
217
+ let numMatch;
218
+ while ((numMatch = numPattern.exec(refs)) !== null) {
219
+ deps.add(parseInt(numMatch[1], 10));
220
+ }
221
+ }
222
+ return Array.from(deps).sort((a, b) => a - b);
223
+ }
224
+ function storeDependencies(issueNumber, dependsOn) {
225
+ if (!dependsOn || dependsOn.length === 0) return { added: 0, existing: 0 };
226
+ const crossRefsPath = path.join(getMgwDir(), "cross-refs.json");
227
+ let crossRefs = { links: [] };
228
+ if (fs.existsSync(crossRefsPath)) {
229
+ try {
230
+ crossRefs = JSON.parse(fs.readFileSync(crossRefsPath, "utf-8"));
231
+ if (!Array.isArray(crossRefs.links)) crossRefs.links = [];
232
+ } catch {
233
+ crossRefs = { links: [] };
234
+ }
235
+ }
236
+ let added = 0;
237
+ let existing = 0;
238
+ const issueRef = `#${issueNumber}`;
239
+ for (const dep of dependsOn) {
240
+ const depRef = `#${dep}`;
241
+ const alreadyExists = crossRefs.links.some(
242
+ (l) => l.a === issueRef && l.b === depRef && l.type === "blocked-by"
243
+ );
244
+ if (alreadyExists) {
245
+ existing++;
246
+ continue;
247
+ }
248
+ crossRefs.links.push({
249
+ a: issueRef,
250
+ b: depRef,
251
+ type: "blocked-by",
252
+ created: (/* @__PURE__ */ new Date()).toISOString()
253
+ });
254
+ added++;
255
+ }
256
+ if (added > 0) {
257
+ const mgwDir = getMgwDir();
258
+ if (!fs.existsSync(mgwDir)) {
259
+ fs.mkdirSync(mgwDir, { recursive: true });
260
+ }
261
+ fs.writeFileSync(crossRefsPath, JSON.stringify(crossRefs, null, 2), "utf-8");
262
+ }
263
+ return { added, existing };
264
+ }
265
+ function topologicalSort(issues, links) {
266
+ if (!issues || issues.length === 0) return [];
267
+ if (!links || links.length === 0) return [...issues];
268
+ const deps = /* @__PURE__ */ new Map();
269
+ const issueSet = new Set(issues.map((i) => i.number));
270
+ for (const issue of issues) {
271
+ deps.set(issue.number, /* @__PURE__ */ new Set());
272
+ }
273
+ for (const link of links) {
274
+ if (link.type !== "blocked-by") continue;
275
+ const aMatch = String(link.a).match(/#(\d+)/);
276
+ const bMatch = String(link.b).match(/#(\d+)/);
277
+ if (!aMatch || !bMatch) continue;
278
+ const a = parseInt(aMatch[1], 10);
279
+ const b = parseInt(bMatch[1], 10);
280
+ if (issueSet.has(a) && issueSet.has(b) && deps.has(a)) {
281
+ deps.get(a).add(b);
282
+ }
283
+ }
284
+ const inDegree = /* @__PURE__ */ new Map();
285
+ for (const issue of issues) {
286
+ inDegree.set(issue.number, deps.get(issue.number).size);
287
+ }
288
+ const queue = [];
289
+ for (const [num, degree] of inDegree) {
290
+ if (degree === 0) queue.push(num);
291
+ }
292
+ const issueMap = new Map(issues.map((i) => [i.number, i]));
293
+ const sorted = [];
294
+ while (queue.length > 0) {
295
+ queue.sort((a, b) => {
296
+ const idxA = issues.findIndex((i) => i.number === a);
297
+ const idxB = issues.findIndex((i) => i.number === b);
298
+ return idxA - idxB;
299
+ });
300
+ const num = queue.shift();
301
+ sorted.push(issueMap.get(num));
302
+ for (const [dependent, depSet] of deps) {
303
+ if (depSet.has(num)) {
304
+ depSet.delete(num);
305
+ inDegree.set(dependent, inDegree.get(dependent) - 1);
306
+ if (inDegree.get(dependent) === 0) {
307
+ queue.push(dependent);
308
+ }
309
+ }
310
+ }
311
+ }
312
+ if (sorted.length < issues.length) {
313
+ for (const issue of issues) {
314
+ if (!sorted.includes(issue)) {
315
+ sorted.push(issue);
316
+ }
317
+ }
318
+ }
319
+ return sorted;
320
+ }
160
321
  state = {
161
322
  getMgwDir,
162
323
  getActiveDir,
@@ -166,34 +327,312 @@ function requireState () {
166
327
  loadActiveIssue,
167
328
  mergeProjectState,
168
329
  migrateProjectState,
169
- resolveActiveMilestoneIndex
330
+ resolveActiveMilestoneIndex,
331
+ loadCrossRefs,
332
+ VALID_LINK_TYPES,
333
+ parseDependencies,
334
+ storeDependencies,
335
+ topologicalSort
170
336
  };
171
337
  return state;
172
338
  }
173
339
 
340
+ var errors;
341
+ var hasRequiredErrors;
342
+
343
+ function requireErrors () {
344
+ if (hasRequiredErrors) return errors;
345
+ hasRequiredErrors = 1;
346
+ class MgwError extends Error {
347
+ /**
348
+ * @param {string} message
349
+ * @param {object} [opts]
350
+ * @param {string} [opts.code] - Machine-readable error code
351
+ * @param {string} [opts.stage] - Pipeline stage where error occurred
352
+ * @param {number} [opts.issueNumber] - Related GitHub issue number
353
+ * @param {Error} [opts.cause] - Original error
354
+ */
355
+ constructor(message, opts) {
356
+ super(message);
357
+ this.name = "MgwError";
358
+ const o = opts || {};
359
+ this.code = o.code || "MGW_ERROR";
360
+ this.stage = o.stage || null;
361
+ this.issueNumber = o.issueNumber || null;
362
+ if (o.cause) this.cause = o.cause;
363
+ }
364
+ }
365
+ class GitHubApiError extends MgwError {
366
+ /**
367
+ * @param {string} message
368
+ * @param {object} [opts]
369
+ * @param {number} [opts.status] - HTTP status code
370
+ * @param {string} [opts.endpoint] - API endpoint or gh subcommand
371
+ */
372
+ constructor(message, opts) {
373
+ const o = opts || {};
374
+ super(message, { code: "GITHUB_API_ERROR", ...o });
375
+ this.name = "GitHubApiError";
376
+ this.status = o.status || null;
377
+ this.endpoint = o.endpoint || null;
378
+ }
379
+ }
380
+ class GsdToolError extends MgwError {
381
+ /**
382
+ * @param {string} message
383
+ * @param {object} [opts]
384
+ * @param {string} [opts.command] - GSD subcommand that failed
385
+ */
386
+ constructor(message, opts) {
387
+ const o = opts || {};
388
+ super(message, { code: "GSD_TOOL_ERROR", ...o });
389
+ this.name = "GsdToolError";
390
+ this.command = o.command || null;
391
+ }
392
+ }
393
+ class StateError extends MgwError {
394
+ /**
395
+ * @param {string} message
396
+ * @param {object} [opts]
397
+ * @param {string} [opts.filePath] - State file path
398
+ */
399
+ constructor(message, opts) {
400
+ const o = opts || {};
401
+ super(message, { code: "STATE_ERROR", ...o });
402
+ this.name = "StateError";
403
+ this.filePath = o.filePath || null;
404
+ }
405
+ }
406
+ class TimeoutError extends MgwError {
407
+ /**
408
+ * @param {string} message
409
+ * @param {object} [opts]
410
+ * @param {number} [opts.timeoutMs] - Timeout duration in milliseconds
411
+ * @param {string} [opts.operation] - Description of the timed-out operation
412
+ */
413
+ constructor(message, opts) {
414
+ const o = opts || {};
415
+ super(message, { code: "TIMEOUT_ERROR", ...o });
416
+ this.name = "TimeoutError";
417
+ this.timeoutMs = o.timeoutMs || null;
418
+ this.operation = o.operation || null;
419
+ }
420
+ }
421
+ class ClaudeNotAvailableError extends MgwError {
422
+ /**
423
+ * @param {string} message
424
+ * @param {object} [opts]
425
+ * @param {'not-installed'|'not-authenticated'|'check-failed'} [opts.reason]
426
+ */
427
+ constructor(message, opts) {
428
+ const o = opts || {};
429
+ super(message, { code: "CLAUDE_NOT_AVAILABLE", ...o });
430
+ this.name = "ClaudeNotAvailableError";
431
+ this.reason = o.reason || null;
432
+ }
433
+ }
434
+ errors = {
435
+ MgwError,
436
+ GitHubApiError,
437
+ GsdToolError,
438
+ StateError,
439
+ TimeoutError,
440
+ ClaudeNotAvailableError
441
+ };
442
+ return errors;
443
+ }
444
+
445
+ var retry;
446
+ var hasRequiredRetry;
447
+
448
+ function requireRetry () {
449
+ if (hasRequiredRetry) return retry;
450
+ hasRequiredRetry = 1;
451
+ const MAX_RETRIES = 3;
452
+ const BACKOFF_BASE_MS = 5e3;
453
+ const BACKOFF_MAX_MS = 3e5;
454
+ const TRANSIENT_STATUS_CODES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
455
+ const TRANSIENT_MESSAGE_PATTERNS = [
456
+ "network timeout",
457
+ "econnreset",
458
+ "econnrefused",
459
+ "etimedout",
460
+ "socket hang up",
461
+ "worktree lock",
462
+ "model overload",
463
+ "rate limit",
464
+ "too many requests",
465
+ "service unavailable",
466
+ "bad gateway",
467
+ "gateway timeout"
468
+ ];
469
+ const NEEDS_INFO_MESSAGE_PATTERNS = [
470
+ "ambiguous",
471
+ "missing required field",
472
+ "contradictory requirements",
473
+ "issue body"
474
+ ];
475
+ function classifyFailure(error) {
476
+ if (!error || typeof error !== "object") {
477
+ return { class: "permanent", reason: "no error object provided" };
478
+ }
479
+ const status = error.status;
480
+ const message = (error.message || "").toLowerCase();
481
+ const code = (error.code || "").toLowerCase();
482
+ if (typeof status === "number") {
483
+ if (status === 429) {
484
+ return { class: "transient", reason: "rate limit (HTTP 429)" };
485
+ }
486
+ if (TRANSIENT_STATUS_CODES.has(status)) {
487
+ return { class: "transient", reason: `server error (HTTP ${status})` };
488
+ }
489
+ if (status === 403) {
490
+ return { class: "permanent", reason: "forbidden (HTTP 403 \u2014 non-rate-limit)" };
491
+ }
492
+ if (status >= 400 && status < 500) {
493
+ return { class: "permanent", reason: `client error (HTTP ${status})` };
494
+ }
495
+ }
496
+ if (code) {
497
+ const networkCodes = /* @__PURE__ */ new Set([
498
+ "econnreset",
499
+ "econnrefused",
500
+ "etimedout",
501
+ "enotfound",
502
+ "epipe"
503
+ ]);
504
+ if (networkCodes.has(code)) {
505
+ return { class: "transient", reason: `network error (${code.toUpperCase()})` };
506
+ }
507
+ if (code === "enoent") {
508
+ return { class: "permanent", reason: "file not found (ENOENT) \u2014 GSD tools may be missing" };
509
+ }
510
+ }
511
+ for (const pattern of TRANSIENT_MESSAGE_PATTERNS) {
512
+ if (message.includes(pattern)) {
513
+ return { class: "transient", reason: `transient condition detected: "${pattern}"` };
514
+ }
515
+ }
516
+ for (const pattern of NEEDS_INFO_MESSAGE_PATTERNS) {
517
+ if (message.includes(pattern)) {
518
+ return { class: "needs-info", reason: `issue requires clarification: "${pattern}"` };
519
+ }
520
+ }
521
+ return {
522
+ class: "permanent",
523
+ reason: "unknown error \u2014 classified as permanent to prevent runaway retries"
524
+ };
525
+ }
526
+ function canRetry(issueState) {
527
+ if (!issueState || typeof issueState !== "object") return false;
528
+ if (issueState.dead_letter === true) return false;
529
+ const count = typeof issueState.retry_count === "number" ? issueState.retry_count : 0;
530
+ return count < MAX_RETRIES;
531
+ }
532
+ function incrementRetry(issueState) {
533
+ const current = typeof issueState.retry_count === "number" ? issueState.retry_count : 0;
534
+ return Object.assign({}, issueState, { retry_count: current + 1 });
535
+ }
536
+ function resetRetryState(issueState) {
537
+ return Object.assign({}, issueState, {
538
+ retry_count: 0,
539
+ last_failure_class: null,
540
+ dead_letter: false
541
+ });
542
+ }
543
+ function getBackoffMs(retryCount) {
544
+ const count = Math.max(0, Math.floor(retryCount));
545
+ const base = Math.min(BACKOFF_MAX_MS, BACKOFF_BASE_MS * Math.pow(2, count));
546
+ return Math.floor(Math.random() * (base + 1));
547
+ }
548
+ async function withRetry(fn, opts) {
549
+ const o = opts || {};
550
+ const maxAttempts = (typeof o.maxRetries === "number" ? o.maxRetries : MAX_RETRIES) + 1;
551
+ let lastError;
552
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
553
+ try {
554
+ return await fn();
555
+ } catch (err) {
556
+ lastError = err;
557
+ if (attempt >= maxAttempts - 1) break;
558
+ const classification = classifyFailure(err);
559
+ if (classification.class !== "transient") break;
560
+ const delayMs = getBackoffMs(attempt);
561
+ if (delayMs > 0) {
562
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
563
+ }
564
+ }
565
+ }
566
+ throw lastError;
567
+ }
568
+ retry = {
569
+ // Constants
570
+ MAX_RETRIES,
571
+ BACKOFF_BASE_MS,
572
+ BACKOFF_MAX_MS,
573
+ // Core functions
574
+ classifyFailure,
575
+ canRetry,
576
+ incrementRetry,
577
+ resetRetryState,
578
+ getBackoffMs,
579
+ withRetry
580
+ };
581
+ return retry;
582
+ }
583
+
174
584
  var github;
175
585
  var hasRequiredGithub;
176
586
 
177
587
  function requireGithub () {
178
588
  if (hasRequiredGithub) return github;
179
589
  hasRequiredGithub = 1;
180
- const { execSync } = require$$0$1;
590
+ const { execSync } = require$$0;
591
+ const { TimeoutError, GitHubApiError } = requireErrors();
592
+ const { withRetry } = requireRetry();
593
+ const GH_TIMEOUT_MS = 3e4;
594
+ function parseHttpStatus(stderr) {
595
+ if (!stderr) return null;
596
+ const match = stderr.match(/HTTP (\d{3})/);
597
+ return match ? parseInt(match[1], 10) : null;
598
+ }
181
599
  function run(cmd) {
182
- return execSync(cmd, {
183
- encoding: "utf-8",
184
- stdio: ["pipe", "pipe", "pipe"]
185
- }).trim();
600
+ try {
601
+ return execSync(cmd, {
602
+ encoding: "utf-8",
603
+ stdio: ["pipe", "pipe", "pipe"],
604
+ timeout: GH_TIMEOUT_MS
605
+ }).trim();
606
+ } catch (err) {
607
+ if (err.killed) {
608
+ throw new TimeoutError(
609
+ `gh command timed out after ${GH_TIMEOUT_MS / 1e3}s: ${cmd.slice(0, 80)}`,
610
+ { timeoutMs: GH_TIMEOUT_MS, operation: cmd.slice(0, 120) }
611
+ );
612
+ }
613
+ const stderr = (err.stderr || "").trim();
614
+ const httpStatus = parseHttpStatus(stderr);
615
+ const ghErr = new GitHubApiError(stderr || err.message, {
616
+ cause: err,
617
+ status: httpStatus
618
+ });
619
+ if (err.code) ghErr.code = err.code;
620
+ throw ghErr;
621
+ }
622
+ }
623
+ async function runWithRetry(cmd) {
624
+ return withRetry(async () => run(cmd));
186
625
  }
187
- function getRepo() {
188
- return run("gh repo view --json nameWithOwner -q .nameWithOwner");
626
+ async function getRepo() {
627
+ return runWithRetry("gh repo view --json nameWithOwner -q .nameWithOwner");
189
628
  }
190
- function getIssue(number) {
191
- const raw = run(
629
+ async function getIssue(number) {
630
+ const raw = await runWithRetry(
192
631
  `gh issue view ${number} --json number,title,state,labels,milestone,assignees,body`
193
632
  );
194
633
  return JSON.parse(raw);
195
634
  }
196
- function listIssues(filters) {
635
+ async function listIssues(filters) {
197
636
  const f = filters || {};
198
637
  let cmd = "gh issue list --json number,title,state,labels,milestone,assignees,createdAt,url,body,comments";
199
638
  if (f.label) cmd += ` --label ${JSON.stringify(f.label)}`;
@@ -201,16 +640,16 @@ function requireGithub () {
201
640
  if (f.assignee && f.assignee !== "all") cmd += ` --assignee ${JSON.stringify(f.assignee)}`;
202
641
  if (f.state) cmd += ` --state ${f.state}`;
203
642
  if (f.limit) cmd += ` --limit ${parseInt(f.limit, 10)}`;
204
- const raw = run(cmd);
643
+ const raw = await runWithRetry(cmd);
205
644
  return JSON.parse(raw);
206
645
  }
207
- function getMilestone(number) {
208
- const repo = getRepo();
209
- const raw = run(`gh api repos/${repo}/milestones/${number}`);
646
+ async function getMilestone(number) {
647
+ const repo = await getRepo();
648
+ const raw = await runWithRetry(`gh api repos/${repo}/milestones/${number}`);
210
649
  return JSON.parse(raw);
211
650
  }
212
- function getRateLimit() {
213
- const raw = run("gh api rate_limit");
651
+ async function getRateLimit() {
652
+ const raw = await runWithRetry("gh api rate_limit");
214
653
  const data = JSON.parse(raw);
215
654
  const core = data.resources.core;
216
655
  return {
@@ -219,21 +658,21 @@ function requireGithub () {
219
658
  reset: core.reset
220
659
  };
221
660
  }
222
- function closeMilestone(repo, number) {
223
- const raw = run(
661
+ async function closeMilestone(repo, number) {
662
+ const raw = await runWithRetry(
224
663
  `gh api repos/${repo}/milestones/${number} --method PATCH -f state=closed`
225
664
  );
226
665
  return JSON.parse(raw);
227
666
  }
228
- function createRelease(repo, tag, title, opts) {
667
+ async function createRelease(repo, tag, title, opts) {
229
668
  const o = opts || {};
230
669
  let cmd = `gh release create ${JSON.stringify(tag)} --repo ${JSON.stringify(repo)} --title ${JSON.stringify(title)}`;
231
670
  if (o.notes) cmd += ` --notes ${JSON.stringify(o.notes)}`;
232
671
  if (o.draft) cmd += " --draft";
233
672
  if (o.prerelease) cmd += " --prerelease";
234
- return run(cmd);
673
+ return runWithRetry(cmd);
235
674
  }
236
- function getProjectNodeId(owner, projectNumber) {
675
+ async function getProjectNodeId(owner, projectNumber) {
237
676
  const userQuery = `'query($login: String!, $number: Int!) { user(login: $login) { projectV2(number: $number) { id } } }'`;
238
677
  const orgQuery = `'query($login: String!, $number: Int!) { organization(login: $login) { projectV2(number: $number) { id } } }'`;
239
678
  try {
@@ -252,7 +691,7 @@ function requireGithub () {
252
691
  }
253
692
  return null;
254
693
  }
255
- function findExistingBoard(owner, titlePattern) {
694
+ async function findExistingBoard(owner, titlePattern) {
256
695
  const pattern = titlePattern.toLowerCase();
257
696
  try {
258
697
  const raw = run(
@@ -274,7 +713,7 @@ function requireGithub () {
274
713
  }
275
714
  return null;
276
715
  }
277
- function getProjectFields(owner, projectNumber) {
716
+ async function getProjectFields(owner, projectNumber) {
278
717
  const query = `'query($login: String!, $number: Int!) { user(login: $login) { projectV2(number: $number) { fields(first: 20) { nodes { ... on ProjectV2SingleSelectField { id name options { id name } } ... on ProjectV2Field { id name dataType } } } } } }'`;
279
718
  const orgQuery = `'query($login: String!, $number: Int!) { organization(login: $login) { projectV2(number: $number) { fields(first: 20) { nodes { ... on ProjectV2SingleSelectField { id name options { id name } } ... on ProjectV2Field { id name dataType } } } } } }'`;
280
719
  let raw;
@@ -352,19 +791,19 @@ function requireGithub () {
352
791
  }
353
792
  return Object.keys(fields).length > 0 ? fields : null;
354
793
  }
355
- function createProject(owner, title) {
356
- const raw = run(
794
+ async function createProject(owner, title) {
795
+ const raw = await runWithRetry(
357
796
  `gh project create --owner ${JSON.stringify(owner)} --title ${JSON.stringify(title)} --format json`
358
797
  );
359
798
  const data = JSON.parse(raw);
360
799
  return { number: data.number, url: data.url };
361
800
  }
362
- function addItemToProject(owner, projectNumber, issueUrl) {
363
- return run(
801
+ async function addItemToProject(owner, projectNumber, issueUrl) {
802
+ return runWithRetry(
364
803
  `gh project item-add ${projectNumber} --owner ${JSON.stringify(owner)} --url ${JSON.stringify(issueUrl)}`
365
804
  );
366
805
  }
367
- function postMilestoneStartAnnouncement(opts) {
806
+ async function postMilestoneStartAnnouncement(opts) {
368
807
  const {
369
808
  repo,
370
809
  milestoneName,
@@ -435,6 +874,8 @@ function requireGithub () {
435
874
  return { posted: false, method: "none", url: null };
436
875
  }
437
876
  github = {
877
+ GH_TIMEOUT_MS,
878
+ run,
438
879
  getRepo,
439
880
  getIssue,
440
881
  listIssues,
@@ -517,15 +958,17 @@ function requireOutput () {
517
958
  return output;
518
959
  }
519
960
 
520
- var claude;
521
- var hasRequiredClaude;
961
+ var providerClaude;
962
+ var hasRequiredProviderClaude;
522
963
 
523
- function requireClaude () {
524
- if (hasRequiredClaude) return claude;
525
- hasRequiredClaude = 1;
526
- const { execSync, spawn } = require$$0$1;
964
+ function requireProviderClaude () {
965
+ if (hasRequiredProviderClaude) return providerClaude;
966
+ hasRequiredProviderClaude = 1;
967
+ const { execSync, spawn } = require$$0;
527
968
  const path = require$$1;
528
- const fs = require$$0;
969
+ const fs = require$$2;
970
+ const { ClaudeNotAvailableError, TimeoutError } = requireErrors();
971
+ const PROVIDER_ID = "claude";
529
972
  function getCommandsDir() {
530
973
  const dir = path.join(__dirname, "..", "commands");
531
974
  if (!fs.existsSync(dir)) {
@@ -536,37 +979,51 @@ This may indicate a corrupted installation. Try reinstalling mgw.`
536
979
  }
537
980
  return dir;
538
981
  }
539
- function assertClaudeAvailable() {
982
+ function assertAvailable() {
540
983
  try {
541
984
  execSync("claude --version", {
542
985
  encoding: "utf-8",
543
- stdio: ["pipe", "pipe", "pipe"]
986
+ stdio: ["pipe", "pipe", "pipe"],
987
+ timeout: 1e4
544
988
  });
545
989
  } catch (err) {
546
- if (err.code === "ENOENT") {
547
- console.error(
548
- "Error: claude CLI is not installed.\n\nInstall it with:\n npm install -g @anthropic-ai/claude-code\n\nThen run:\n claude login"
990
+ if (err.killed) {
991
+ throw new TimeoutError(
992
+ "claude --version timed out after 10s",
993
+ { timeoutMs: 1e4, operation: "claude --version" }
549
994
  );
550
- } else {
551
- console.error(
552
- "Error: claude CLI check failed.\nEnsure claude is installed and on your PATH."
995
+ }
996
+ if (err.code === "ENOENT") {
997
+ throw new ClaudeNotAvailableError(
998
+ "claude CLI is not installed.\n\nInstall it with:\n npm install -g @anthropic-ai/claude-code\n\nThen run:\n claude login",
999
+ { reason: "not-installed" }
553
1000
  );
554
1001
  }
555
- process.exit(1);
1002
+ throw new ClaudeNotAvailableError(
1003
+ "claude CLI check failed.\nEnsure claude is installed and on your PATH.",
1004
+ { reason: "check-failed", cause: err }
1005
+ );
556
1006
  }
557
1007
  try {
558
1008
  execSync("claude auth status", {
559
1009
  encoding: "utf-8",
560
- stdio: ["pipe", "pipe", "pipe"]
1010
+ stdio: ["pipe", "pipe", "pipe"],
1011
+ timeout: 1e4
561
1012
  });
562
- } catch {
563
- console.error(
564
- "Error: claude CLI is not authenticated.\n\nRun:\n claude login\n\nThen retry your command."
1013
+ } catch (err) {
1014
+ if (err.killed) {
1015
+ throw new TimeoutError(
1016
+ "claude auth status timed out after 10s",
1017
+ { timeoutMs: 1e4, operation: "claude auth status" }
1018
+ );
1019
+ }
1020
+ throw new ClaudeNotAvailableError(
1021
+ "claude CLI is not authenticated.\n\nRun:\n claude login\n\nThen retry your command.",
1022
+ { reason: "not-authenticated" }
565
1023
  );
566
- process.exit(1);
567
1024
  }
568
1025
  }
569
- function invokeClaude(commandFile, userPrompt, opts) {
1026
+ function invoke(commandFile, userPrompt, opts) {
570
1027
  const o = opts || {};
571
1028
  const args = ["-p"];
572
1029
  if (commandFile) {
@@ -587,6 +1044,10 @@ This may indicate a corrupted installation. Try reinstalling mgw.`
587
1044
  const stdio = o.quiet ? ["pipe", "pipe", "pipe"] : ["inherit", "inherit", "inherit"];
588
1045
  const child = spawn("claude", args, { stdio });
589
1046
  let output = "";
1047
+ const sigintHandler = () => {
1048
+ child.kill("SIGINT");
1049
+ };
1050
+ process.on("SIGINT", sigintHandler);
590
1051
  if (o.quiet) {
591
1052
  child.stdout.on("data", (chunk) => {
592
1053
  output += chunk.toString();
@@ -596,23 +1057,220 @@ This may indicate a corrupted installation. Try reinstalling mgw.`
596
1057
  });
597
1058
  }
598
1059
  child.on("error", (err) => {
1060
+ process.removeListener("SIGINT", sigintHandler);
599
1061
  if (err.code === "ENOENT") {
600
- reject(new Error("claude CLI not found. Install with: npm install -g @anthropic-ai/claude-code"));
1062
+ reject(new ClaudeNotAvailableError(
1063
+ "claude CLI not found. Install with: npm install -g @anthropic-ai/claude-code",
1064
+ { reason: "not-installed" }
1065
+ ));
601
1066
  } else {
602
1067
  reject(err);
603
1068
  }
604
1069
  });
605
1070
  child.on("close", (code) => {
1071
+ process.removeListener("SIGINT", sigintHandler);
1072
+ resolve({ exitCode: code || 0, output });
1073
+ });
1074
+ });
1075
+ }
1076
+ providerClaude = { PROVIDER_ID, assertAvailable, invoke, getCommandsDir };
1077
+ return providerClaude;
1078
+ }
1079
+
1080
+ var providerGemini;
1081
+ var hasRequiredProviderGemini;
1082
+
1083
+ function requireProviderGemini () {
1084
+ if (hasRequiredProviderGemini) return providerGemini;
1085
+ hasRequiredProviderGemini = 1;
1086
+ const { execSync, spawn } = require$$0;
1087
+ const path = require$$1;
1088
+ const fs = require$$2;
1089
+ const os = require$$3;
1090
+ const PROVIDER_ID = "gemini";
1091
+ function getCommandsDir() {
1092
+ const dir = path.join(os.homedir(), ".gemini", "commands", "mgw");
1093
+ if (!fs.existsSync(dir)) {
1094
+ throw new Error(
1095
+ `Commands directory not found at: ${dir}
1096
+ Run: node bin/mgw-install.cjs --provider gemini
1097
+ (or reinstall the mgw package to trigger postinstall)`
1098
+ );
1099
+ }
1100
+ return dir;
1101
+ }
1102
+ function assertAvailable() {
1103
+ try {
1104
+ execSync("gemini --version", {
1105
+ encoding: "utf-8",
1106
+ stdio: ["pipe", "pipe", "pipe"]
1107
+ });
1108
+ } catch (err) {
1109
+ if (err.code === "ENOENT") {
1110
+ console.error(
1111
+ "Error: gemini CLI is not installed.\n\nInstall it with:\n npm install -g @google/gemini-cli\n\nThen run:\n gemini auth"
1112
+ );
1113
+ } else {
1114
+ console.error(
1115
+ "Error: gemini CLI check failed.\nEnsure gemini is installed and on your PATH."
1116
+ );
1117
+ }
1118
+ process.exit(1);
1119
+ }
1120
+ }
1121
+ function invoke(commandFile, userPrompt, opts) {
1122
+ const o = opts || {};
1123
+ if (o.dryRun) {
1124
+ const dryArgs = ["-p"];
1125
+ if (o.model) dryArgs.push("--model", o.model);
1126
+ dryArgs.push(commandFile ? "<system>" + commandFile + "</system> " + (userPrompt || "run") : userPrompt || "run");
1127
+ console.log("Would invoke: gemini " + dryArgs.join(" "));
1128
+ return Promise.resolve({ exitCode: 0, output: "" });
1129
+ }
1130
+ let effectivePrompt = userPrompt || "run";
1131
+ if (commandFile) {
1132
+ const fileContents = fs.readFileSync(commandFile, "utf-8");
1133
+ effectivePrompt = "<system>\n" + fileContents + "\n</system>\n\n" + effectivePrompt;
1134
+ }
1135
+ const args = ["-p"];
1136
+ if (o.model) {
1137
+ args.push("--model", o.model);
1138
+ }
1139
+ args.push(effectivePrompt);
1140
+ return new Promise((resolve, reject) => {
1141
+ const stdio = o.quiet ? ["pipe", "pipe", "pipe"] : ["inherit", "inherit", "inherit"];
1142
+ const child = spawn("gemini", args, { stdio });
1143
+ let output = "";
1144
+ if (o.quiet) {
1145
+ child.stdout.on("data", function(chunk) {
1146
+ output += chunk.toString();
1147
+ });
1148
+ child.stderr.on("data", function(chunk) {
1149
+ output += chunk.toString();
1150
+ });
1151
+ }
1152
+ child.on("error", function(err) {
1153
+ if (err.code === "ENOENT") {
1154
+ reject(new Error("gemini CLI not found. Install with: npm install -g @google/gemini-cli"));
1155
+ } else {
1156
+ reject(err);
1157
+ }
1158
+ });
1159
+ child.on("close", function(code) {
606
1160
  resolve({ exitCode: code || 0, output });
607
1161
  });
608
1162
  });
609
1163
  }
610
- claude = {
611
- assertClaudeAvailable,
612
- invokeClaude,
613
- getCommandsDir
1164
+ providerGemini = { PROVIDER_ID, assertAvailable, invoke, getCommandsDir };
1165
+ return providerGemini;
1166
+ }
1167
+
1168
+ var providerOpencode;
1169
+ var hasRequiredProviderOpencode;
1170
+
1171
+ function requireProviderOpencode () {
1172
+ if (hasRequiredProviderOpencode) return providerOpencode;
1173
+ hasRequiredProviderOpencode = 1;
1174
+ const { execSync, spawn } = require$$0;
1175
+ const path = require$$1;
1176
+ const fs = require$$2;
1177
+ const os = require$$3;
1178
+ const PROVIDER_ID = "opencode";
1179
+ function getCommandsDir() {
1180
+ const dir = path.join(os.homedir(), ".opencode", "commands", "mgw");
1181
+ if (!fs.existsSync(dir)) {
1182
+ throw new Error(
1183
+ "Commands directory not found at: " + dir + "\nRun: node bin/mgw-install.cjs --provider opencode\n(or reinstall the mgw package to trigger postinstall)"
1184
+ );
1185
+ }
1186
+ return dir;
1187
+ }
1188
+ function assertAvailable() {
1189
+ try {
1190
+ execSync("opencode --version", {
1191
+ encoding: "utf-8",
1192
+ stdio: ["pipe", "pipe", "pipe"]
1193
+ });
1194
+ } catch (err) {
1195
+ if (err.code === "ENOENT") {
1196
+ console.error(
1197
+ "Error: opencode CLI is not installed.\n\nInstall it with:\n npm install -g opencode-ai\n\n(or see https://opencode.ai for installation instructions)"
1198
+ );
1199
+ } else {
1200
+ console.error(
1201
+ "Error: opencode CLI check failed.\nEnsure opencode is installed and on your PATH."
1202
+ );
1203
+ }
1204
+ process.exit(1);
1205
+ }
1206
+ }
1207
+ function invoke(commandFile, userPrompt, opts) {
1208
+ const o = opts || {};
1209
+ const args = ["run"];
1210
+ if (commandFile) {
1211
+ args.push("--system-prompt", commandFile);
1212
+ }
1213
+ if (o.model) {
1214
+ args.push("--model", o.model);
1215
+ }
1216
+ args.push(userPrompt || "run");
1217
+ if (o.dryRun) {
1218
+ console.log("Would invoke: opencode " + args.join(" "));
1219
+ return Promise.resolve({ exitCode: 0, output: "" });
1220
+ }
1221
+ return new Promise(function(resolve, reject) {
1222
+ const stdio = o.quiet ? ["pipe", "pipe", "pipe"] : ["inherit", "inherit", "inherit"];
1223
+ const child = spawn("opencode", args, { stdio });
1224
+ let output = "";
1225
+ if (o.quiet) {
1226
+ child.stdout.on("data", function(chunk) {
1227
+ output += chunk.toString();
1228
+ });
1229
+ child.stderr.on("data", function(chunk) {
1230
+ output += chunk.toString();
1231
+ });
1232
+ }
1233
+ child.on("error", function(err) {
1234
+ if (err.code === "ENOENT") {
1235
+ reject(new Error("opencode CLI not found. See https://opencode.ai for installation instructions."));
1236
+ } else {
1237
+ reject(err);
1238
+ }
1239
+ });
1240
+ child.on("close", function(code) {
1241
+ resolve({ exitCode: code || 0, output });
1242
+ });
1243
+ });
1244
+ }
1245
+ providerOpencode = { PROVIDER_ID, assertAvailable, invoke, getCommandsDir };
1246
+ return providerOpencode;
1247
+ }
1248
+
1249
+ var providerManager;
1250
+ var hasRequiredProviderManager;
1251
+
1252
+ function requireProviderManager () {
1253
+ if (hasRequiredProviderManager) return providerManager;
1254
+ hasRequiredProviderManager = 1;
1255
+ const registry = {
1256
+ claude: requireProviderClaude(),
1257
+ gemini: requireProviderGemini(),
1258
+ opencode: requireProviderOpencode()
614
1259
  };
615
- return claude;
1260
+ function getProvider(providerId) {
1261
+ const id = providerId || "claude";
1262
+ const provider = registry[id];
1263
+ if (!provider) {
1264
+ const available = Object.keys(registry).join(", ");
1265
+ throw new Error(`Unknown provider: "${id}". Available: ${available}`);
1266
+ }
1267
+ return provider;
1268
+ }
1269
+ function listProviders() {
1270
+ return Object.keys(registry);
1271
+ }
1272
+ providerManager = { ProviderManager: { getProvider, listProviders } };
1273
+ return providerManager;
616
1274
  }
617
1275
 
618
1276
  var spinner;
@@ -716,6 +1374,160 @@ function requireSpinner () {
716
1374
  return spinner;
717
1375
  }
718
1376
 
1377
+ var logger;
1378
+ var hasRequiredLogger;
1379
+
1380
+ function requireLogger () {
1381
+ if (hasRequiredLogger) return logger;
1382
+ hasRequiredLogger = 1;
1383
+ const path = require$$1;
1384
+ const fs = require$$2;
1385
+ function getLogDir(repoRoot) {
1386
+ const root = repoRoot || process.cwd();
1387
+ const logDir = path.join(root, ".mgw", "logs");
1388
+ if (!fs.existsSync(logDir)) {
1389
+ fs.mkdirSync(logDir, { recursive: true });
1390
+ }
1391
+ return logDir;
1392
+ }
1393
+ function getLogFile(repoRoot) {
1394
+ const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
1395
+ return path.join(getLogDir(repoRoot), `${date}.jsonl`);
1396
+ }
1397
+ function writeLog(entry) {
1398
+ const { repoRoot, ...rest } = entry;
1399
+ const record = {
1400
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1401
+ ...rest
1402
+ };
1403
+ try {
1404
+ const logFile = getLogFile(repoRoot);
1405
+ fs.appendFileSync(logFile, JSON.stringify(record) + "\n");
1406
+ } catch {
1407
+ }
1408
+ }
1409
+ function startTimer(entry) {
1410
+ const start = Date.now();
1411
+ return {
1412
+ finish(status, errorMsg) {
1413
+ writeLog({
1414
+ ...entry,
1415
+ duration_ms: Date.now() - start,
1416
+ status,
1417
+ error: errorMsg || void 0
1418
+ });
1419
+ }
1420
+ };
1421
+ }
1422
+ function readLogs(opts) {
1423
+ const o = opts || {};
1424
+ const logDir = getLogDir(o.repoRoot);
1425
+ if (!fs.existsSync(logDir)) return [];
1426
+ let sinceDate = null;
1427
+ if (o.since) {
1428
+ const relativeMatch = o.since.match(/^(\d+)d$/);
1429
+ if (relativeMatch) {
1430
+ sinceDate = /* @__PURE__ */ new Date();
1431
+ sinceDate.setDate(sinceDate.getDate() - parseInt(relativeMatch[1], 10));
1432
+ } else {
1433
+ sinceDate = new Date(o.since);
1434
+ }
1435
+ }
1436
+ let files;
1437
+ try {
1438
+ files = fs.readdirSync(logDir).filter((f) => f.endsWith(".jsonl")).sort();
1439
+ } catch {
1440
+ return [];
1441
+ }
1442
+ if (sinceDate) {
1443
+ const sinceStr = sinceDate.toISOString().slice(0, 10);
1444
+ files = files.filter((f) => f.replace(".jsonl", "") >= sinceStr);
1445
+ }
1446
+ const entries = [];
1447
+ for (const file of files) {
1448
+ const filePath = path.join(logDir, file);
1449
+ let content;
1450
+ try {
1451
+ content = fs.readFileSync(filePath, "utf-8");
1452
+ } catch {
1453
+ continue;
1454
+ }
1455
+ for (const line of content.split("\n")) {
1456
+ if (!line.trim()) continue;
1457
+ let entry;
1458
+ try {
1459
+ entry = JSON.parse(line);
1460
+ } catch {
1461
+ continue;
1462
+ }
1463
+ if (o.issue && entry.issue !== o.issue) continue;
1464
+ if (o.command && entry.command !== o.command) continue;
1465
+ if (o.stage && entry.stage !== o.stage) continue;
1466
+ entries.push(entry);
1467
+ }
1468
+ }
1469
+ entries.reverse();
1470
+ if (o.limit && entries.length > o.limit) {
1471
+ return entries.slice(0, o.limit);
1472
+ }
1473
+ return entries;
1474
+ }
1475
+ function aggregateMetrics(entries) {
1476
+ if (!entries || entries.length === 0) {
1477
+ return {
1478
+ total: 0,
1479
+ byStatus: {},
1480
+ byCommand: {},
1481
+ avgDuration: 0,
1482
+ failureRate: 0
1483
+ };
1484
+ }
1485
+ const byStatus = {};
1486
+ const byCommand = {};
1487
+ let totalDuration = 0;
1488
+ let durationCount = 0;
1489
+ let failures = 0;
1490
+ for (const e of entries) {
1491
+ byStatus[e.status] = (byStatus[e.status] || 0) + 1;
1492
+ if (e.status === "error") failures++;
1493
+ if (e.command) {
1494
+ if (!byCommand[e.command]) {
1495
+ byCommand[e.command] = { count: 0, errors: 0, totalDuration: 0 };
1496
+ }
1497
+ byCommand[e.command].count++;
1498
+ if (e.status === "error") byCommand[e.command].errors++;
1499
+ if (typeof e.duration_ms === "number") {
1500
+ byCommand[e.command].totalDuration += e.duration_ms;
1501
+ }
1502
+ }
1503
+ if (typeof e.duration_ms === "number") {
1504
+ totalDuration += e.duration_ms;
1505
+ durationCount++;
1506
+ }
1507
+ }
1508
+ for (const cmd of Object.keys(byCommand)) {
1509
+ const c = byCommand[cmd];
1510
+ c.avgDuration = c.count > 0 ? Math.round(c.totalDuration / c.count) : 0;
1511
+ }
1512
+ return {
1513
+ total: entries.length,
1514
+ byStatus,
1515
+ byCommand,
1516
+ avgDuration: durationCount > 0 ? Math.round(totalDuration / durationCount) : 0,
1517
+ failureRate: entries.length > 0 ? Math.round(failures / entries.length * 100) : 0
1518
+ };
1519
+ }
1520
+ logger = {
1521
+ getLogDir,
1522
+ getLogFile,
1523
+ writeLog,
1524
+ startTimer,
1525
+ readLogs,
1526
+ aggregateMetrics
1527
+ };
1528
+ return logger;
1529
+ }
1530
+
719
1531
  var renderer;
720
1532
  var hasRequiredRenderer;
721
1533
 
@@ -1189,7 +2001,7 @@ var hasRequiredKeyboard;
1189
2001
  function requireKeyboard () {
1190
2002
  if (hasRequiredKeyboard) return keyboard;
1191
2003
  hasRequiredKeyboard = 1;
1192
- const { EventEmitter } = require$$0$2;
2004
+ const { EventEmitter } = require$$0$1;
1193
2005
  const DEFAULT_BINDINGS = {
1194
2006
  // Scroll
1195
2007
  "j": "scroll-down",
@@ -1545,7 +2357,7 @@ function requireGraceful () {
1545
2357
  let ms;
1546
2358
  try {
1547
2359
  ms = Date.now() - new Date(dateStr).getTime();
1548
- } catch (e) {
2360
+ } catch (_e) {
1549
2361
  return "-";
1550
2362
  }
1551
2363
  if (ms < 0) return "now";
@@ -1808,9 +2620,13 @@ function requireTui () {
1808
2620
  }
1809
2621
 
1810
2622
  exports.getDefaultExportFromCjs = getDefaultExportFromCjs;
1811
- exports.requireClaude = requireClaude;
2623
+ exports.requireErrors = requireErrors;
1812
2624
  exports.requireGithub = requireGithub;
2625
+ exports.requireLogger = requireLogger;
1813
2626
  exports.requireOutput = requireOutput;
2627
+ exports.requireProviderClaude = requireProviderClaude;
2628
+ exports.requireProviderManager = requireProviderManager;
2629
+ exports.requireRetry = requireRetry;
1814
2630
  exports.requireSpinner = requireSpinner;
1815
2631
  exports.requireState = requireState;
1816
2632
  exports.requireTui = requireTui;