patchrelay 0.35.11 → 0.35.13

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 (52) hide show
  1. package/README.md +41 -9
  2. package/dist/build-info.json +3 -3
  3. package/dist/cli/args.js +19 -1
  4. package/dist/cli/commands/issues.js +18 -56
  5. package/dist/cli/commands/watch.js +5 -0
  6. package/dist/cli/data.js +160 -47
  7. package/dist/cli/formatters/text.js +51 -90
  8. package/dist/cli/help.js +15 -8
  9. package/dist/cli/index.js +3 -58
  10. package/dist/cli/operator-client.js +0 -82
  11. package/dist/cli/watch/App.js +21 -12
  12. package/dist/cli/watch/HelpBar.js +3 -3
  13. package/dist/cli/watch/IssueDetailView.js +63 -130
  14. package/dist/cli/watch/IssueRow.js +82 -27
  15. package/dist/cli/watch/StatusBar.js +8 -4
  16. package/dist/cli/watch/detail-rows.js +589 -0
  17. package/dist/cli/watch/render-rich-text.js +226 -0
  18. package/dist/cli/watch/state-visualization.js +48 -23
  19. package/dist/cli/watch/timeline-builder.js +2 -1
  20. package/dist/cli/watch/use-detail-stream.js +10 -104
  21. package/dist/cli/watch/use-watch-stream.js +11 -102
  22. package/dist/cli/watch/watch-state.js +129 -56
  23. package/dist/codex-thread-utils.js +3 -0
  24. package/dist/db/migrations.js +239 -2
  25. package/dist/db.js +628 -39
  26. package/dist/github-app-token.js +7 -0
  27. package/dist/github-failure-context.js +44 -1
  28. package/dist/github-rollup.js +47 -0
  29. package/dist/github-webhook-handler.js +423 -52
  30. package/dist/github-webhooks.js +7 -0
  31. package/dist/http.js +12 -264
  32. package/dist/idle-reconciliation.js +268 -76
  33. package/dist/issue-query-service.js +221 -129
  34. package/dist/issue-session-events.js +151 -0
  35. package/dist/issue-session.js +99 -0
  36. package/dist/linear-client.js +39 -25
  37. package/dist/linear-session-reporting.js +12 -0
  38. package/dist/linear-session-sync.js +253 -24
  39. package/dist/linear-workflow.js +33 -0
  40. package/dist/merge-queue-protocol.js +0 -51
  41. package/dist/preflight.js +1 -4
  42. package/dist/queue-health-monitor.js +11 -7
  43. package/dist/run-orchestrator.js +1364 -147
  44. package/dist/run-reporting.js +5 -3
  45. package/dist/service.js +279 -102
  46. package/dist/status-note.js +56 -0
  47. package/dist/waiting-reason.js +65 -0
  48. package/dist/webhook-handler.js +270 -79
  49. package/package.json +3 -2
  50. package/dist/cli/commands/feed.js +0 -60
  51. package/dist/cli/watch/FeedView.js +0 -28
  52. package/dist/cli/watch/use-feed-stream.js +0 -92
@@ -57,6 +57,11 @@ fi
57
57
  exec /usr/bin/gh "$@"
58
58
  `;
59
59
  await writeFile(ghWrapper, script, { mode: 0o755 });
60
+ const currentPath = process.env.PATH ?? "";
61
+ const pathEntries = currentPath.split(path.delimiter).filter(Boolean);
62
+ if (!pathEntries.includes(binDir)) {
63
+ process.env.PATH = [binDir, ...pathEntries].join(path.delimiter);
64
+ }
60
65
  logger.debug({ path: ghWrapper }, "Wrote gh wrapper script");
61
66
  }
62
67
  /**
@@ -144,6 +149,8 @@ export function createGitHubAppTokenManager(credentials, logger) {
144
149
  await mkdir(path.dirname(tokenFile), { recursive: true });
145
150
  await writeFile(tokenFile, token, { mode: 0o600 });
146
151
  cachedToken = token;
152
+ process.env.GH_TOKEN = token;
153
+ process.env.GITHUB_TOKEN = token;
147
154
  logger.debug("Refreshed GitHub App installation token");
148
155
  }
149
156
  catch (error) {
@@ -33,7 +33,14 @@ export function createGitHubFailureContextResolver() {
33
33
  const annotations = failedCheck?.id
34
34
  ? await resolveAnnotations(repoFullName, failedCheck.id)
35
35
  : undefined;
36
- const summary = firstNonEmpty(annotations?.[0], failedCheck?.outputTitle, failedCheck?.outputSummary, event.checkOutputTitle, event.checkOutputSummary, workflowJob?.stepName ? `Failed step: ${workflowJob.stepName}` : undefined);
36
+ const summary = pickFailureSummary({
37
+ annotations,
38
+ failedCheckOutputTitle: failedCheck?.outputTitle,
39
+ failedCheckOutputSummary: failedCheck?.outputSummary,
40
+ eventCheckOutputTitle: event.checkOutputTitle,
41
+ eventCheckOutputSummary: event.checkOutputSummary,
42
+ workflowStepName: workflowJob?.stepName,
43
+ });
37
44
  const checkName = firstNonEmpty(failedCheck?.name, event.checkName);
38
45
  const checkUrl = firstNonEmpty(failedCheck?.htmlUrl, event.checkUrl);
39
46
  const checkDetailsUrl = firstNonEmpty(failedCheck?.detailsUrl, event.checkDetailsUrl);
@@ -105,6 +112,12 @@ export function summarizeGitHubFailureContext(context) {
105
112
  const step = context.stepName ? `${lead ?? "CI"} -> ${context.stepName}` : lead;
106
113
  return firstNonEmpty(step && context.summary ? `${step}: ${context.summary}` : undefined, step, context.summary);
107
114
  }
115
+ export function pickFailureSummary(params) {
116
+ const preferredAnnotation = pickPreferredFailureAnnotation(params.annotations);
117
+ const structuredSummary = firstNonEmpty(params.failedCheckOutputTitle, params.failedCheckOutputSummary, params.eventCheckOutputTitle, params.eventCheckOutputSummary);
118
+ const failedStepSummary = params.workflowStepName ? `Failed step: ${params.workflowStepName}` : undefined;
119
+ return firstNonEmpty(preferredAnnotation, structuredSummary, failedStepSummary, params.annotations?.[0]);
120
+ }
108
121
  function buildFallbackFailureContext(source, repoFullName, event) {
109
122
  const summary = firstNonEmpty(event.checkOutputTitle, event.checkOutputSummary, event.checkOutputText ? sanitizeDiagnosticText(event.checkOutputText, 240) : undefined);
110
123
  return {
@@ -119,6 +132,36 @@ function buildFallbackFailureContext(source, repoFullName, event) {
119
132
  ...(summary ? { summary } : {}),
120
133
  };
121
134
  }
135
+ export function pickPreferredFailureAnnotation(annotations) {
136
+ if (!Array.isArray(annotations) || annotations.length === 0)
137
+ return undefined;
138
+ const ranked = annotations
139
+ .map((annotation) => ({ annotation, score: scoreFailureAnnotation(annotation) }))
140
+ .filter((entry) => entry.score > 0)
141
+ .sort((left, right) => right.score - left.score);
142
+ return ranked[0]?.annotation;
143
+ }
144
+ function scoreFailureAnnotation(annotation) {
145
+ const text = annotation.trim();
146
+ if (!text)
147
+ return 0;
148
+ const lower = text.toLowerCase();
149
+ if (lower.startsWith("process completed with exit code"))
150
+ return 0;
151
+ if (lower.includes("actions target node.js 20 but are being forced to run on node.js 24"))
152
+ return 0;
153
+ let score = 1;
154
+ if (!lower.includes("(.github)")) {
155
+ score += 2;
156
+ }
157
+ if (lower.includes("assertionerror") || lower.includes("expected values to be strictly equal")) {
158
+ score += 2;
159
+ }
160
+ if (lower.includes("error") || lower.includes("exception") || lower.includes("failed")) {
161
+ score += 1;
162
+ }
163
+ return score;
164
+ }
122
165
  async function resolveFailedCheckRun(repoFullName, event) {
123
166
  if (!event.headSha)
124
167
  return undefined;
@@ -0,0 +1,47 @@
1
+ const FAILED_CONCLUSIONS = new Set([
2
+ "action_required",
3
+ "cancelled",
4
+ "failure",
5
+ "stale",
6
+ "startup_failure",
7
+ "timed_out",
8
+ ]);
9
+ function normalizeGateStatus(entry) {
10
+ const status = entry.status?.trim().toLowerCase();
11
+ if (status === "queued" || status === "in_progress" || status === "requested" || status === "waiting" || status === "pending") {
12
+ return "pending";
13
+ }
14
+ const conclusion = entry.conclusion?.trim().toLowerCase();
15
+ if (conclusion === "success" || conclusion === "neutral" || conclusion === "skipped") {
16
+ return "success";
17
+ }
18
+ if (conclusion && FAILED_CONCLUSIONS.has(conclusion)) {
19
+ return "failure";
20
+ }
21
+ return status === "completed" ? "failure" : "pending";
22
+ }
23
+ export function deriveGateCheckStatusFromRollup(statusCheckRollup, gateCheckNames) {
24
+ if (!Array.isArray(statusCheckRollup) || statusCheckRollup.length === 0) {
25
+ return undefined;
26
+ }
27
+ const expectedNames = gateCheckNames
28
+ .map((entry) => entry.trim().toLowerCase())
29
+ .filter(Boolean);
30
+ if (expectedNames.length === 0) {
31
+ return undefined;
32
+ }
33
+ const matches = statusCheckRollup.filter((entry) => {
34
+ if (typeof entry?.name !== "string" || !entry.name.trim())
35
+ return false;
36
+ return expectedNames.includes(entry.name.trim().toLowerCase());
37
+ });
38
+ if (matches.length === 0) {
39
+ return undefined;
40
+ }
41
+ const normalized = matches.map((entry) => normalizeGateStatus(entry));
42
+ if (normalized.some((status) => status === "pending"))
43
+ return "pending";
44
+ if (normalized.some((status) => status === "failure"))
45
+ return "failure";
46
+ return "success";
47
+ }