opencode-forgekeeper 2.0.0 → 2.1.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.
Files changed (4) hide show
  1. package/README.md +10 -3
  2. package/dist/index.js +252 -217
  3. package/dist/tui.js +444 -101
  4. package/package.json +1 -1
package/dist/tui.js CHANGED
@@ -69,10 +69,14 @@ class ForgeError extends Error {
69
69
  }
70
70
  }
71
71
 
72
+ // packages/forgekeeper/src/graphql.ts
73
+ var query = (text) => text;
74
+ var typed = (transport) => async (host, query2, variables) => await transport(host, query2, variables);
75
+
72
76
  // packages/forgekeeper/src/gitlab-feedback.ts
73
77
  var PAGE_SIZE = 100;
74
78
  var MAX_COMMENT_PAGES = 20;
75
- var COMMENTS_QUERY = `query($project: ID!, $iid: String!, $before: String) {
79
+ var COMMENTS_QUERY = query(`query($project: ID!, $iid: String!, $before: String) {
76
80
  currentUser { id }
77
81
  project(fullPath: $project) {
78
82
  mergeRequest(iid: $iid) {
@@ -86,7 +90,7 @@ var COMMENTS_QUERY = `query($project: ID!, $iid: String!, $before: String) {
86
90
  }
87
91
  }
88
92
  }
89
- }`;
93
+ }`);
90
94
  var noteNumber = (id) => id.slice(id.lastIndexOf("/") + 1);
91
95
  var timestamp = (node) => Date.parse(node.lastEditedAt ?? node.updatedAt ?? node.createdAt);
92
96
  function toComment(node) {
@@ -105,7 +109,8 @@ function toComment(node) {
105
109
  username: node.author.username
106
110
  };
107
111
  }
108
- async function fetchComments(graphql, host, project, iid) {
112
+ async function fetchComments(transport, host, project, iid) {
113
+ const graphql = typed(transport);
109
114
  const pages = [];
110
115
  let viewerID;
111
116
  let before;
@@ -119,8 +124,8 @@ async function fetchComments(graphql, host, project, iid) {
119
124
  if (before)
120
125
  variables.before = before;
121
126
  const data = await graphql(host, COMMENTS_QUERY, variables);
122
- viewerID ??= data?.currentUser?.id ?? undefined;
123
- const connection = data?.project?.mergeRequest?.notes;
127
+ viewerID ??= data.currentUser?.id ?? undefined;
128
+ const connection = data.project?.mergeRequest?.notes;
124
129
  if (!connection)
125
130
  return page === 0 ? undefined : snapshot(false);
126
131
  pages.push(connection.nodes.flatMap((node) => toComment(node) ?? []));
@@ -152,17 +157,17 @@ function mergeRequestsQuery(projectCount) {
152
157
  mergeRequests(sourceBranches: [$branch], state: opened, first: 20) { nodes { ${MERGE_REQUEST_FIELDS} } }
153
158
  }`).join(`
154
159
  `);
155
- return `query($branch: String!, ${variables}) {
160
+ return query(`query($branch: String!, ${variables}) {
156
161
  ${projects}
157
- }`;
162
+ }`);
158
163
  }
159
164
  function mergeRequestQuery(projectCount) {
160
165
  const variables = Array.from({ length: projectCount }, (_, index) => `$p${index}: ID!`).join(", ");
161
166
  const projects = Array.from({ length: projectCount }, (_, index) => `p${index}: project(fullPath: $p${index}) { mergeRequest(iid: $iid) { ${MERGE_REQUEST_FIELDS} } }`).join(`
162
167
  `);
163
- return `query($iid: String!, ${variables}) {
168
+ return query(`query($iid: String!, ${variables}) {
164
169
  ${projects}
165
- }`;
170
+ }`);
166
171
  }
167
172
  function mergeRequestNumbersQuery(projectCount) {
168
173
  const variables = Array.from({ length: projectCount }, (_, index) => `$p${index}: ID!`).join(", ");
@@ -170,18 +175,18 @@ function mergeRequestNumbersQuery(projectCount) {
170
175
  mergeRequests(sourceBranches: [$branch], state: opened, first: 20) { nodes { iid updatedAt sourceProject { fullPath } } }
171
176
  }`).join(`
172
177
  `);
173
- return `query($branch: String!, ${variables}) {
178
+ return query(`query($branch: String!, ${variables}) {
174
179
  ${projects}
175
- }`;
180
+ }`);
176
181
  }
177
- var SOURCE_BRANCH_QUERY = `query($project: ID!, $iid: String!) {
182
+ var SOURCE_BRANCH_QUERY = query(`query($project: ID!, $iid: String!) {
178
183
  project(fullPath: $project) { mergeRequest(iid: $iid) { sourceBranch } }
179
- }`;
180
- var DISCUSSIONS_QUERY = `query($project: ID!, $iid: String!, $after: String) {
184
+ }`);
185
+ var DISCUSSIONS_QUERY = query(`query($project: ID!, $iid: String!, $after: String) {
181
186
  project(fullPath: $project) {
182
187
  mergeRequest(iid: $iid) { discussions(first: ${PAGE_SIZE2}, after: $after) { ${DISCUSSIONS} } }
183
188
  }
184
- }`;
189
+ }`);
185
190
  function classifyError(message, code) {
186
191
  const text = message.trim().replace(/^glab:\s*/, "") || `glab exited with code ${code}`;
187
192
  if (/ENOENT|command not found/i.test(text))
@@ -196,8 +201,8 @@ function classifyError(message, code) {
196
201
  }
197
202
  var isResponse = (body) => typeof body === "object" && body !== null && (!("errors" in body) || Array.isArray(body.errors)) && (!("data" in body) || typeof body.data === "object");
198
203
  function glabGraphQL(run, cwd) {
199
- return async (host, query, variables) => {
200
- const args = ["api", "graphql", "--hostname", host, "-f", `query=${query}`];
204
+ return async (host, query2, variables) => {
205
+ const args = ["api", "graphql", "--hostname", host, "-f", `query=${query2}`];
201
206
  for (const [name, value] of Object.entries(variables))
202
207
  args.push("-f", `${name}=${value}`);
203
208
  const result = await run("glab", args, cwd);
@@ -265,10 +270,10 @@ function duoState(state) {
265
270
  }
266
271
 
267
272
  class GitLabForge {
268
- graphql;
269
273
  traits = gitlabTraits;
274
+ graphql;
270
275
  constructor(graphql) {
271
- this.graphql = graphql;
276
+ this.graphql = typed(graphql);
272
277
  }
273
278
  async findByBranch(repository) {
274
279
  const { source, targets, sourceBranch } = repository;
@@ -279,7 +284,7 @@ class GitLabForge {
279
284
  const seen = new Set;
280
285
  const nodes = [];
281
286
  targets.forEach((_, index) => {
282
- for (const node of data?.[`p${index}`]?.mergeRequests?.nodes ?? []) {
287
+ for (const node of data[`p${index}`]?.mergeRequests?.nodes ?? []) {
283
288
  if (node.sourceProject?.fullPath.toLowerCase() !== sourcePath)
284
289
  continue;
285
290
  const key = `${node.targetProject.fullPath}!${node.iid}`;
@@ -298,7 +303,7 @@ class GitLabForge {
298
303
  targets.forEach((target, index) => variables[`p${index}`] = target.path);
299
304
  const data = await this.graphql(source.host, mergeRequestNumbersQuery(targets.length), variables);
300
305
  const sourcePath = source.path.toLowerCase();
301
- const nodes = targets.flatMap((_, index) => data?.[`p${index}`]?.mergeRequests?.nodes ?? []);
306
+ const nodes = targets.flatMap((_, index) => data[`p${index}`]?.mergeRequests?.nodes ?? []);
302
307
  return nodes.filter((node) => node.sourceProject?.fullPath.toLowerCase() === sourcePath).sort((a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? ""))[0]?.iid;
303
308
  }
304
309
  async sourceBranch(ref) {
@@ -306,7 +311,7 @@ class GitLabForge {
306
311
  project: ref.project,
307
312
  iid: ref.iid
308
313
  });
309
- const branch = data?.project?.mergeRequest?.sourceBranch;
314
+ const branch = data.project?.mergeRequest?.sourceBranch;
310
315
  return typeof branch === "string" && branch ? branch : undefined;
311
316
  }
312
317
  async findByNumber(projects, iid) {
@@ -317,7 +322,7 @@ class GitLabForge {
317
322
  projects.forEach((project, index) => variables[`p${index}`] = project.path);
318
323
  const data = await this.graphql(host, mergeRequestQuery(projects.length), variables);
319
324
  for (let index = 0;index < projects.length; index++) {
320
- const node = data?.[`p${index}`]?.mergeRequest ?? undefined;
325
+ const node = data[`p${index}`]?.mergeRequest;
321
326
  if (node)
322
327
  return this.normalize(host, node);
323
328
  }
@@ -333,7 +338,7 @@ class GitLabForge {
333
338
  iid: node.iid,
334
339
  after: page.endCursor
335
340
  });
336
- const next = data?.project?.mergeRequest?.discussions;
341
+ const next = data.project?.mergeRequest?.discussions;
337
342
  if (!next)
338
343
  break;
339
344
  count += countUnresolved(next);
@@ -389,8 +394,8 @@ var fields = `number title url state isDraft mergeable reviewDecision headRefOid
389
394
  } } } } }`;
390
395
  var MAX_PAGES = 50;
391
396
  function ghGraphQL(run, cwd) {
392
- return async (host, query, variables) => {
393
- const args = ["api", "graphql", "--hostname", host, "-f", `query=${query}`];
397
+ return async (host, query2, variables) => {
398
+ const args = ["api", "graphql", "--hostname", host, "-f", `query=${query2}`];
394
399
  for (const [name, value] of Object.entries(variables))
395
400
  args.push(typeof value === "number" ? "-F" : "-f", `${name}=${value}`);
396
401
  const result = await run("gh", args, cwd);
@@ -480,17 +485,17 @@ var githubTraits = {
480
485
  };
481
486
 
482
487
  class GitHubForge {
483
- graphql;
484
488
  traits = githubTraits;
489
+ graphql;
485
490
  constructor(graphql) {
486
- this.graphql = graphql;
491
+ this.graphql = typed(graphql);
487
492
  }
488
493
  async findByNumber(projects, number) {
489
494
  for (const project of projects) {
490
495
  const [owner, repo] = project.path.split("/");
491
- const data = await this.graphql(project.host, `query($owner:String!,$repo:String!,$number:Int!) {
496
+ const data = await this.graphql(project.host, query(`query($owner:String!,$repo:String!,$number:Int!) {
492
497
  repository(owner:$owner,name:$repo) { pullRequest(number:$number) { ${fields} } }
493
- }`, { owner, repo, number: Number(number) });
498
+ }`), { owner, repo, number: Number(number) });
494
499
  const node = data.repository?.pullRequest;
495
500
  if (node)
496
501
  return this.normalize(project.host, node);
@@ -501,10 +506,10 @@ class GitHubForge {
501
506
  let newest;
502
507
  for (const target of repository.targets) {
503
508
  const [owner, repo] = target.path.split("/");
504
- const data = await this.graphql(target.host, `query($owner:String!,$repo:String!,$branch:String!) {
509
+ const data = await this.graphql(target.host, query(`query($owner:String!,$repo:String!,$branch:String!) {
505
510
  repository(owner:$owner,name:$repo) { pullRequests(first:100,states:OPEN,headRefName:$branch,orderBy:{field:UPDATED_AT,direction:DESC}) {
506
511
  nodes { number updatedAt headRepository { nameWithOwner } }
507
- } } }`, { owner, repo, branch: repository.sourceBranch });
512
+ } } }`), { owner, repo, branch: repository.sourceBranch });
508
513
  const nodes = data.repository?.pullRequests?.nodes ?? [];
509
514
  for (const node of nodes) {
510
515
  if (node.headRepository?.nameWithOwner.toLowerCase() !== repository.source.path.toLowerCase())
@@ -517,11 +522,10 @@ class GitHubForge {
517
522
  }
518
523
  async sourceBranch(ref) {
519
524
  const [owner, repo] = ref.project.split("/");
520
- const data = await this.graphql(ref.host, `query($owner:String!,$repo:String!,$number:Int!) {
525
+ const data = await this.graphql(ref.host, query(`query($owner:String!,$repo:String!,$number:Int!) {
521
526
  repository(owner:$owner,name:$repo) { pullRequest(number:$number) { headRefName } }
522
- }`, { owner, repo, number: Number(ref.iid) });
523
- const branch = data.repository?.pullRequest?.headRefName;
524
- return typeof branch === "string" && branch ? branch : undefined;
527
+ }`), { owner, repo, number: Number(ref.iid) });
528
+ return data.repository?.pullRequest?.headRefName || undefined;
525
529
  }
526
530
  async findByBranch(repository) {
527
531
  const found = new Map;
@@ -530,10 +534,10 @@ class GitHubForge {
530
534
  let after;
531
535
  let complete = false;
532
536
  for (let page = 0;page < MAX_PAGES; page++) {
533
- const data = await this.graphql(target.host, `query($owner:String!,$repo:String!,$branch:String!${after ? ",$after:String!" : ""}) {
537
+ const data = await this.graphql(target.host, query(`query($owner:String!,$repo:String!,$branch:String!${after ? ",$after:String!" : ""}) {
534
538
  repository(owner:$owner,name:$repo) { pullRequests(first:100,states:OPEN,headRefName:$branch${after ? ",after:$after" : ""}) {
535
539
  nodes { ${fields} } pageInfo { hasNextPage endCursor }
536
- } } }`, { owner, repo, branch: repository.sourceBranch, ...after ? { after } : {} });
540
+ } } }`), { owner, repo, branch: repository.sourceBranch, ...after ? { after } : {} });
537
541
  const pulls = data.repository?.pullRequests;
538
542
  if (!pulls)
539
543
  throw new ForgeError("request", "GitHub repository unavailable");
@@ -561,10 +565,10 @@ class GitHubForge {
561
565
  const threads = [...node.reviewThreads.nodes];
562
566
  let page = node.reviewThreads.pageInfo;
563
567
  for (let count = 1;page?.hasNextPage && page.endCursor && count < MAX_PAGES; count++) {
564
- const data = await this.graphql(host, `query($owner:String!,$repo:String!,$number:Int!,$after:String!) {
568
+ const data = await this.graphql(host, query(`query($owner:String!,$repo:String!,$number:Int!,$after:String!) {
565
569
  repository(owner:$owner,name:$repo) { pullRequest(number:$number) {
566
570
  reviewThreads(first:100,after:$after) { nodes { isResolved } pageInfo { hasNextPage endCursor } }
567
- } } }`, { owner, repo, number: node.number, after: page.endCursor });
571
+ } } }`), { owner, repo, number: node.number, after: page.endCursor });
568
572
  const next = data.repository?.pullRequest?.reviewThreads;
569
573
  if (!next)
570
574
  break;
@@ -582,10 +586,10 @@ class GitHubForge {
582
586
  const contexts = [...rollup?.nodes ?? []];
583
587
  let cursor = rollup?.pageInfo;
584
588
  for (let count = 1;cursor?.hasNextPage && cursor.endCursor && count < MAX_PAGES; count++) {
585
- const data = await this.graphql(host, `query($owner:String!,$repo:String!,$oid:GitObjectID!,$after:String!) {
589
+ const data = await this.graphql(host, query(`query($owner:String!,$repo:String!,$oid:GitObjectID!,$after:String!) {
586
590
  repository(owner:$owner,name:$repo) { object(oid:$oid) { ... on Commit { statusCheckRollup {
587
591
  contexts(first:100,after:$after) { nodes { __typename ... on CheckRun { status conclusion detailsUrl } ... on StatusContext { state targetUrl } } pageInfo { hasNextPage endCursor } }
588
- } } } } }`, { owner, repo, oid: node.headRefOid, after: cursor.endCursor });
592
+ } } } } }`), { owner, repo, oid: node.headRefOid, after: cursor.endCursor });
589
593
  const next = data.repository?.object?.statusCheckRollup?.contexts;
590
594
  if (!next)
591
595
  break;
@@ -727,7 +731,7 @@ function footerSegments(snapshot) {
727
731
  const { lookup, error } = snapshot;
728
732
  const stale = error ? [{ text: "stale", tone: "warning", essential: true }] : [];
729
733
  if (!lookup || lookup.kind === "none" || lookup.requests.length === 0) {
730
- return error ? [{ text: "PR/MR status unavailable", tone: "warning" }] : [];
734
+ return snapshot.targeted && (error || lookup?.explicitTarget) ? [{ text: "PR/MR status unavailable", tone: "warning" }] : [];
731
735
  }
732
736
  if (lookup.failed?.length) {
733
737
  stale.unshift({
@@ -876,11 +880,12 @@ var target = {
876
880
  additionalProperties: false
877
881
  };
878
882
  var key = { type: "string" };
879
- var snapshot = { type: "object" };
883
+ var snapshot = { type: "object", title: "Snapshot" };
880
884
  var status = {
881
885
  type: "object",
882
886
  properties: { enabled: { type: "boolean" }, snapshot },
883
- required: ["enabled", "snapshot"]
887
+ required: ["enabled", "snapshot"],
888
+ additionalProperties: false
884
889
  };
885
890
  var ForgeSessionRpc = {
886
891
  id: "opencode-forgekeeper",
@@ -907,8 +912,12 @@ var ForgeSessionRpc = {
907
912
  },
908
913
  output: {
909
914
  type: "object",
910
- properties: { enabled: { type: "boolean" }, statuses: { type: "object" } },
911
- required: ["enabled", "statuses"]
915
+ properties: {
916
+ enabled: { type: "boolean" },
917
+ statuses: { type: "object", additionalProperties: snapshot }
918
+ },
919
+ required: ["enabled", "statuses"],
920
+ additionalProperties: false
912
921
  }
913
922
  },
914
923
  release: {
@@ -918,7 +927,7 @@ var ForgeSessionRpc = {
918
927
  required: ["clientID"],
919
928
  additionalProperties: false
920
929
  },
921
- output: { type: "object" }
930
+ output: { type: "object", additionalProperties: false }
922
931
  },
923
932
  refresh: {
924
933
  input: {
@@ -943,7 +952,8 @@ var ForgeSessionRpc = {
943
952
  schema: {
944
953
  type: "object",
945
954
  properties: { directory: { type: "string" }, key, snapshot },
946
- required: ["directory", "key", "snapshot"]
955
+ required: ["directory", "key", "snapshot"],
956
+ additionalProperties: false
947
957
  }
948
958
  },
949
959
  notice: {
@@ -955,7 +965,8 @@ var ForgeSessionRpc = {
955
965
  message: { type: "string" },
956
966
  variant: { type: "string", enum: ["info", "error"] }
957
967
  },
958
- required: ["message", "variant"]
968
+ required: ["message", "variant"],
969
+ additionalProperties: false
959
970
  }
960
971
  }
961
972
  }
@@ -1272,6 +1283,337 @@ var tui_default = {
1272
1283
  });
1273
1284
  return true;
1274
1285
  };
1286
+ function DetailField(props) {
1287
+ const foreground = () => props.tone ? color(props.tone) : context.theme.text.base;
1288
+ return (() => {
1289
+ var _el$8 = _$createElement("text"), _el$9 = _$createTextNode(`: `), _el$0 = _$createElement("span");
1290
+ _$insertNode(_el$8, _el$9);
1291
+ _$insertNode(_el$8, _el$0);
1292
+ _$setProp(_el$8, "onMouseUp", () => props.url && openUrl(props.url));
1293
+ _$insert(_el$8, () => props.label, _el$9);
1294
+ _$insert(_el$0, _$createComponent(Show, {
1295
+ get when() {
1296
+ return props.url;
1297
+ },
1298
+ get fallback() {
1299
+ return (() => {
1300
+ var _el$1 = _$createElement("b");
1301
+ _$insert(_el$1, () => props.value);
1302
+ return _el$1;
1303
+ })();
1304
+ },
1305
+ children: (url) => (() => {
1306
+ var _el$10 = _$createElement("a"), _el$11 = _$createElement("u"), _el$12 = _$createElement("b");
1307
+ _$insertNode(_el$10, _el$11);
1308
+ _$insertNode(_el$11, _el$12);
1309
+ _$insert(_el$12, () => props.value);
1310
+ _$effect((_$p) => _$setProp(_el$10, "href", url(), _$p));
1311
+ return _el$10;
1312
+ })()
1313
+ }));
1314
+ _$effect((_p$) => {
1315
+ var _v$3 = context.theme.text.muted, _v$4 = {
1316
+ fg: foreground()
1317
+ };
1318
+ _v$3 !== _p$.e && (_p$.e = _$setProp(_el$8, "fg", _v$3, _p$.e));
1319
+ _v$4 !== _p$.t && (_p$.t = _$setProp(_el$0, "style", _v$4, _p$.t));
1320
+ return _p$;
1321
+ }, {
1322
+ e: undefined,
1323
+ t: undefined
1324
+ });
1325
+ return _el$8;
1326
+ })();
1327
+ }
1328
+ function RequestDetails(props) {
1329
+ const request = () => props.request;
1330
+ const traits = () => traitsOf(request());
1331
+ const review = () => automatedReview(request());
1332
+ return (() => {
1333
+ var _el$13 = _$createElement("box"), _el$14 = _$createElement("text"), _el$15 = _$createElement("a"), _el$16 = _$createElement("u"), _el$17 = _$createElement("b"), _el$18 = _$createTextNode(` `), _el$19 = _$createElement("box"), _el$20 = _$createElement("box"), _el$21 = _$createElement("text"), _el$22 = _$createElement("b");
1334
+ _$insertNode(_el$13, _el$14);
1335
+ _$insertNode(_el$13, _el$19);
1336
+ _$insertNode(_el$13, _el$20);
1337
+ _$setProp(_el$13, "gap", 1);
1338
+ _$setProp(_el$13, "flexShrink", 0);
1339
+ _$insertNode(_el$14, _el$15);
1340
+ _$setProp(_el$14, "onMouseUp", () => openUrl(request().url));
1341
+ _$insertNode(_el$15, _el$16);
1342
+ _$insertNode(_el$16, _el$17);
1343
+ _$insertNode(_el$17, _el$18);
1344
+ _$insert(_el$17, () => reference(request()), _el$18);
1345
+ _$insert(_el$17, () => request().title, null);
1346
+ _$setProp(_el$19, "flexShrink", 0);
1347
+ _$insert(_el$19, _$createComponent(DetailField, {
1348
+ label: "State",
1349
+ get value() {
1350
+ return request().state;
1351
+ },
1352
+ get tone() {
1353
+ return request().state === "merged" ? "success" : undefined;
1354
+ }
1355
+ }), null);
1356
+ _$insert(_el$19, _$createComponent(DetailField, {
1357
+ label: "Target",
1358
+ get value() {
1359
+ return `${request().targetProject} \u2192 ${request().targetBranch}`;
1360
+ }
1361
+ }), null);
1362
+ _$insert(_el$19, _$createComponent(DetailField, {
1363
+ get label() {
1364
+ return traits().ci.charAt(0).toUpperCase() + traits().ci.slice(1);
1365
+ },
1366
+ get value() {
1367
+ return request().pipeline?.label ?? "none";
1368
+ },
1369
+ get tone() {
1370
+ return pipelineTone(request().pipeline);
1371
+ },
1372
+ get url() {
1373
+ return request().pipeline?.url;
1374
+ }
1375
+ }), null);
1376
+ _$insert(_el$19, _$createComponent(DetailField, {
1377
+ label: "Unresolved threads",
1378
+ get value() {
1379
+ return `${request().unresolvedThreads}${request().threadsComplete ? "" : "+"}`;
1380
+ },
1381
+ get tone() {
1382
+ return request().unresolvedThreads || !request().threadsComplete ? "warning" : undefined;
1383
+ }
1384
+ }), null);
1385
+ _$insert(_el$19, _$createComponent(DetailField, {
1386
+ label: "Draft",
1387
+ get value() {
1388
+ return yesNo(request().draft);
1389
+ },
1390
+ get tone() {
1391
+ return request().draft ? "warning" : undefined;
1392
+ }
1393
+ }), null);
1394
+ _$insert(_el$19, _$createComponent(DetailField, {
1395
+ label: "Conflicts",
1396
+ get value() {
1397
+ return _$memo(() => !!request().conflictsKnown)() ? yesNo(request().conflicts) : "unknown";
1398
+ },
1399
+ get tone() {
1400
+ return request().conflicts ? "error" : undefined;
1401
+ }
1402
+ }), null);
1403
+ _$insertNode(_el$20, _el$21);
1404
+ _$setProp(_el$20, "flexShrink", 0);
1405
+ _$insertNode(_el$21, _el$22);
1406
+ _$insertNode(_el$22, _$createTextNode(`Review and approval`));
1407
+ _$insert(_el$20, _$createComponent(For, {
1408
+ get each() {
1409
+ return traits().details(request());
1410
+ },
1411
+ children: (line) => {
1412
+ const separator = line.indexOf(": ");
1413
+ return separator < 0 ? (() => {
1414
+ var _el$29 = _$createElement("text");
1415
+ _$insert(_el$29, line);
1416
+ _$effect((_$p) => _$setProp(_el$29, "fg", context.theme.text.base, _$p));
1417
+ return _el$29;
1418
+ })() : _$createComponent(DetailField, {
1419
+ get label() {
1420
+ return line.slice(0, separator);
1421
+ },
1422
+ get value() {
1423
+ return line.slice(separator + 2);
1424
+ }
1425
+ });
1426
+ }
1427
+ }), null);
1428
+ _$insert(_el$20, _$createComponent(Show, {
1429
+ get when() {
1430
+ return review();
1431
+ },
1432
+ children: (value) => _$createComponent(DetailField, {
1433
+ get label() {
1434
+ return `${value().name} review`;
1435
+ },
1436
+ get value() {
1437
+ return value().label;
1438
+ },
1439
+ get tone() {
1440
+ return value().state === "running" ? "warning" : undefined;
1441
+ }
1442
+ })
1443
+ }), null);
1444
+ _$insert(_el$20, _$createComponent(Show, {
1445
+ get when() {
1446
+ return request().awaitingReviewers.length;
1447
+ },
1448
+ get children() {
1449
+ return _$createComponent(DetailField, {
1450
+ label: "Awaiting review",
1451
+ get value() {
1452
+ return request().awaitingReviewers.map((username) => `@${username}`).join(", ");
1453
+ }
1454
+ });
1455
+ }
1456
+ }), null);
1457
+ _$insert(_el$13, _$createComponent(Show, {
1458
+ get when() {
1459
+ return !request().reviewersComplete;
1460
+ },
1461
+ get children() {
1462
+ var _el$24 = _$createElement("text");
1463
+ _$insertNode(_el$24, _$createTextNode(`Reviewer list is partial`));
1464
+ _$effect((_$p) => _$setProp(_el$24, "fg", color("warning"), _$p));
1465
+ return _el$24;
1466
+ }
1467
+ }), null);
1468
+ _$insert(_el$13, _$createComponent(Show, {
1469
+ get when() {
1470
+ return _$memo(() => !!(props.head && request().headSha))() && props.head !== request().headSha;
1471
+ },
1472
+ get children() {
1473
+ var _el$26 = _$createElement("text"), _el$27 = _$createTextNode(`Local HEAD differs from the `), _el$28 = _$createTextNode(` head commit (unpushed or not fetched)`);
1474
+ _$insertNode(_el$26, _el$27);
1475
+ _$insertNode(_el$26, _el$28);
1476
+ _$insert(_el$26, () => traits().noun, _el$28);
1477
+ _$effect((_$p) => _$setProp(_el$26, "fg", color("warning"), _$p));
1478
+ return _el$26;
1479
+ }
1480
+ }), null);
1481
+ _$effect((_p$) => {
1482
+ var _v$5 = context.theme.text.base, _v$6 = request().url, _v$7 = context.theme.text.base;
1483
+ _v$5 !== _p$.e && (_p$.e = _$setProp(_el$14, "fg", _v$5, _p$.e));
1484
+ _v$6 !== _p$.t && (_p$.t = _$setProp(_el$15, "href", _v$6, _p$.t));
1485
+ _v$7 !== _p$.a && (_p$.a = _$setProp(_el$21, "fg", _v$7, _p$.a));
1486
+ return _p$;
1487
+ }, {
1488
+ e: undefined,
1489
+ t: undefined,
1490
+ a: undefined
1491
+ });
1492
+ return _el$13;
1493
+ })();
1494
+ }
1495
+ function StatusDetails(props) {
1496
+ const lookup = () => {
1497
+ const value = props.snapshot.lookup;
1498
+ return value?.kind === "found" ? value : undefined;
1499
+ };
1500
+ return (() => {
1501
+ var _el$30 = _$createElement("box");
1502
+ _$setProp(_el$30, "gap", 1);
1503
+ _$setProp(_el$30, "flexShrink", 0);
1504
+ _$insert(_el$30, _$createComponent(Show, {
1505
+ get when() {
1506
+ return lookup();
1507
+ },
1508
+ get fallback() {
1509
+ return (() => {
1510
+ var _el$31 = _$createElement("text");
1511
+ _$insert(_el$31, () => detailsMessage(props.snapshot));
1512
+ _$effect((_$p) => _$setProp(_el$31, "fg", props.snapshot.error ? color("error") : context.theme.text.muted, _$p));
1513
+ return _el$31;
1514
+ })();
1515
+ },
1516
+ children: (found) => [_$createComponent(Show, {
1517
+ get when() {
1518
+ return found().repository;
1519
+ },
1520
+ children: (repository) => (() => {
1521
+ var _el$36 = _$createElement("box");
1522
+ _$setProp(_el$36, "flexShrink", 0);
1523
+ _$insert(_el$36, _$createComponent(DetailField, {
1524
+ label: "Repository",
1525
+ get value() {
1526
+ return repository().source.path;
1527
+ }
1528
+ }), null);
1529
+ _$insert(_el$36, _$createComponent(DetailField, {
1530
+ label: "Branch",
1531
+ get value() {
1532
+ return _$memo(() => repository().branch === repository().sourceBranch)() ? repository().branch : `${repository().branch} (pushes to ${repository().sourceBranch})`;
1533
+ }
1534
+ }), null);
1535
+ return _el$36;
1536
+ })()
1537
+ }), _$createComponent(Show, {
1538
+ get when() {
1539
+ return _$memo(() => !!!found().repository)() && found().sessionTarget;
1540
+ },
1541
+ get children() {
1542
+ return _$createComponent(DetailField, {
1543
+ label: "Session target",
1544
+ get value() {
1545
+ return found().sessionTarget;
1546
+ }
1547
+ });
1548
+ }
1549
+ }), _$createComponent(For, {
1550
+ get each() {
1551
+ return found().failed ?? [];
1552
+ },
1553
+ children: (failure) => (() => {
1554
+ var _el$37 = _$createElement("text"), _el$38 = _$createElement("b"), _el$40 = _$createTextNode(`
1555
+ `), _el$41 = _$createTextNode(`
1556
+ `);
1557
+ _$insertNode(_el$37, _el$38);
1558
+ _$insertNode(_el$37, _el$40);
1559
+ _$insertNode(_el$37, _el$41);
1560
+ _$insertNode(_el$38, _$createTextNode(`Unavailable`));
1561
+ _$insert(_el$37, () => failure.url, _el$41);
1562
+ _$insert(_el$37, () => failure.reason, null);
1563
+ _$effect((_$p) => _$setProp(_el$37, "fg", color("warning"), _$p));
1564
+ return _el$37;
1565
+ })()
1566
+ }), _$createComponent(Show, {
1567
+ get when() {
1568
+ return found().requests.length === 0;
1569
+ },
1570
+ get children() {
1571
+ var _el$32 = _$createElement("text");
1572
+ _$insertNode(_el$32, _$createTextNode(`No PR/MR status available.`));
1573
+ _$effect((_$p) => _$setProp(_el$32, "fg", context.theme.text.muted, _$p));
1574
+ return _el$32;
1575
+ }
1576
+ }), _$createComponent(For, {
1577
+ get each() {
1578
+ return found().requests;
1579
+ },
1580
+ children: (request) => _$createComponent(RequestDetails, {
1581
+ request,
1582
+ get head() {
1583
+ return found().repository?.head;
1584
+ }
1585
+ })
1586
+ }), _$createComponent(Show, {
1587
+ get when() {
1588
+ return props.snapshot.error;
1589
+ },
1590
+ children: (error) => (() => {
1591
+ var _el$42 = _$createElement("text"), _el$43 = _$createElement("b"), _el$44 = _$createTextNode(`Last refresh failed at `), _el$45 = _$createTextNode(`
1592
+ `);
1593
+ _$insertNode(_el$42, _el$43);
1594
+ _$insertNode(_el$42, _el$45);
1595
+ _$insertNode(_el$43, _el$44);
1596
+ _$insert(_el$43, () => new Date(error().at).toLocaleTimeString(), null);
1597
+ _$insert(_el$42, () => error().message, null);
1598
+ _$effect((_$p) => _$setProp(_el$42, "fg", color("error"), _$p));
1599
+ return _el$42;
1600
+ })()
1601
+ }), _$createComponent(Show, {
1602
+ get when() {
1603
+ return props.snapshot.fetchedAt !== undefined;
1604
+ },
1605
+ get children() {
1606
+ var _el$34 = _$createElement("text"), _el$35 = _$createTextNode(`Last fetched: `);
1607
+ _$insertNode(_el$34, _el$35);
1608
+ _$insert(_el$34, () => new Date(props.snapshot.fetchedAt).toLocaleTimeString(), null);
1609
+ _$effect((_$p) => _$setProp(_el$34, "fg", context.theme.text.muted, _$p));
1610
+ return _el$34;
1611
+ }
1612
+ })]
1613
+ }));
1614
+ return _el$30;
1615
+ })();
1616
+ }
1275
1617
  function StatusDialog(props) {
1276
1618
  const [terminalHeight, setTerminalHeight] = createSignal(context.renderer.height);
1277
1619
  const onResize = () => setTerminalHeight(context.renderer.height);
@@ -1321,52 +1663,55 @@ var tui_default = {
1321
1663
  }]
1322
1664
  }));
1323
1665
  return (() => {
1324
- var _el$8 = _$createElement("box"), _el$9 = _$createElement("box"), _el$0 = _$createElement("text"), _el$1 = _$createElement("b"), _el$11 = _$createElement("text"), _el$13 = _$createElement("scrollbox"), _el$14 = _$createElement("text"), _el$15 = _$createElement("box");
1325
- _$insertNode(_el$8, _el$9);
1326
- _$insertNode(_el$8, _el$13);
1327
- _$insertNode(_el$8, _el$15);
1328
- _$setProp(_el$8, "paddingLeft", 2);
1329
- _$setProp(_el$8, "paddingRight", 2);
1330
- _$setProp(_el$8, "paddingBottom", 1);
1331
- _$setProp(_el$8, "gap", 1);
1332
- _$insertNode(_el$9, _el$0);
1333
- _$insertNode(_el$9, _el$11);
1334
- _$setProp(_el$9, "height", 1);
1335
- _$setProp(_el$9, "flexShrink", 0);
1336
- _$setProp(_el$9, "flexDirection", "row");
1337
- _$setProp(_el$9, "justifyContent", "space-between");
1338
- _$insertNode(_el$0, _el$1);
1339
- _$insertNode(_el$1, _$createTextNode(`PR/MR status`));
1340
- _$insertNode(_el$11, _$createTextNode(`esc`));
1341
- _$setProp(_el$11, "onMouseUp", close);
1342
- _$insertNode(_el$13, _el$14);
1343
- _$setProp(_el$13, "minHeight", 1);
1344
- _$setProp(_el$13, "flexShrink", 0);
1345
- _$setProp(_el$13, "scrollX", false);
1346
- _$setProp(_el$13, "contentOptions", {
1666
+ var _el$46 = _$createElement("box"), _el$47 = _$createElement("box"), _el$48 = _$createElement("text"), _el$49 = _$createElement("b"), _el$51 = _$createElement("text"), _el$53 = _$createElement("scrollbox"), _el$54 = _$createElement("box");
1667
+ _$insertNode(_el$46, _el$47);
1668
+ _$insertNode(_el$46, _el$53);
1669
+ _$insertNode(_el$46, _el$54);
1670
+ _$setProp(_el$46, "paddingLeft", 2);
1671
+ _$setProp(_el$46, "paddingRight", 2);
1672
+ _$setProp(_el$46, "paddingBottom", 1);
1673
+ _$setProp(_el$46, "gap", 1);
1674
+ _$insertNode(_el$47, _el$48);
1675
+ _$insertNode(_el$47, _el$51);
1676
+ _$setProp(_el$47, "height", 1);
1677
+ _$setProp(_el$47, "flexShrink", 0);
1678
+ _$setProp(_el$47, "flexDirection", "row");
1679
+ _$setProp(_el$47, "justifyContent", "space-between");
1680
+ _$insertNode(_el$48, _el$49);
1681
+ _$insertNode(_el$49, _$createTextNode(`PR/MR status`));
1682
+ _$insertNode(_el$51, _$createTextNode(`esc`));
1683
+ _$setProp(_el$51, "onMouseUp", close);
1684
+ _$setProp(_el$53, "minHeight", 1);
1685
+ _$setProp(_el$53, "flexShrink", 0);
1686
+ _$setProp(_el$53, "scrollX", false);
1687
+ _$setProp(_el$53, "contentOptions", {
1347
1688
  flexShrink: 0
1348
1689
  });
1349
- _$insert(_el$14, () => detailsMessage(snapshot2()));
1350
- _$setProp(_el$15, "height", 1);
1351
- _$setProp(_el$15, "flexShrink", 0);
1352
- _$setProp(_el$15, "flexDirection", "row");
1353
- _$setProp(_el$15, "justifyContent", "flex-end");
1354
- _$insert(_el$15, _$createComponent(For, {
1690
+ _$insert(_el$53, _$createComponent(StatusDetails, {
1691
+ get snapshot() {
1692
+ return snapshot2();
1693
+ }
1694
+ }));
1695
+ _$setProp(_el$54, "height", 1);
1696
+ _$setProp(_el$54, "flexShrink", 0);
1697
+ _$setProp(_el$54, "flexDirection", "row");
1698
+ _$setProp(_el$54, "justifyContent", "flex-end");
1699
+ _$insert(_el$54, _$createComponent(For, {
1355
1700
  each: ["close", "refresh"],
1356
1701
  children: (action) => {
1357
1702
  const disabled = () => action === "refresh" && snapshot2().loading;
1358
1703
  const active = () => activeAction() === action && !disabled();
1359
1704
  return (() => {
1360
- var _el$16 = _$createElement("box"), _el$17 = _$createElement("text");
1361
- _$insertNode(_el$16, _el$17);
1362
- _$setProp(_el$16, "paddingLeft", 1);
1363
- _$setProp(_el$16, "paddingRight", 1);
1364
- _$setProp(_el$16, "flexShrink", 0);
1365
- _$setProp(_el$16, "onMouseMove", () => {
1705
+ var _el$55 = _$createElement("box"), _el$56 = _$createElement("text");
1706
+ _$insertNode(_el$55, _el$56);
1707
+ _$setProp(_el$55, "paddingLeft", 1);
1708
+ _$setProp(_el$55, "paddingRight", 1);
1709
+ _$setProp(_el$55, "flexShrink", 0);
1710
+ _$setProp(_el$55, "onMouseMove", () => {
1366
1711
  if (!disabled())
1367
1712
  setActiveAction(action);
1368
1713
  });
1369
- _$setProp(_el$16, "onMouseUp", () => {
1714
+ _$setProp(_el$55, "onMouseUp", () => {
1370
1715
  if (disabled())
1371
1716
  return;
1372
1717
  setActiveAction(action);
@@ -1375,37 +1720,35 @@ var tui_default = {
1375
1720
  else
1376
1721
  close();
1377
1722
  });
1378
- _$setProp(_el$17, "wrapMode", "none");
1379
- _$insert(_el$17, () => action === "close" ? "Close" : snapshot2().loading ? "Refreshing\u2026" : "Refresh");
1723
+ _$setProp(_el$56, "wrapMode", "none");
1724
+ _$insert(_el$56, () => action === "close" ? "Close" : snapshot2().loading ? "Refreshing\u2026" : "Refresh");
1380
1725
  _$effect((_p$) => {
1381
- var _v$8 = active() ? context.theme.background.action.primary.focused : undefined, _v$9 = disabled() ? context.theme.text.action.primary.disabled : active() ? context.theme.text.action.primary.focused : context.theme.text.muted;
1382
- _v$8 !== _p$.e && (_p$.e = _$setProp(_el$16, "backgroundColor", _v$8, _p$.e));
1383
- _v$9 !== _p$.t && (_p$.t = _$setProp(_el$17, "fg", _v$9, _p$.t));
1726
+ var _v$10 = active() ? context.theme.background.action.primary.focused : undefined, _v$11 = disabled() ? context.theme.text.action.primary.disabled : active() ? context.theme.text.action.primary.focused : context.theme.text.muted;
1727
+ _v$10 !== _p$.e && (_p$.e = _$setProp(_el$55, "backgroundColor", _v$10, _p$.e));
1728
+ _v$11 !== _p$.t && (_p$.t = _$setProp(_el$56, "fg", _v$11, _p$.t));
1384
1729
  return _p$;
1385
1730
  }, {
1386
1731
  e: undefined,
1387
1732
  t: undefined
1388
1733
  });
1389
- return _el$16;
1734
+ return _el$55;
1390
1735
  })();
1391
1736
  }
1392
1737
  }));
1393
1738
  _$effect((_p$) => {
1394
- var _v$3 = contentHeight() + 5, _v$4 = context.theme.text.base, _v$5 = context.theme.text.muted, _v$6 = contentHeight(), _v$7 = context.theme.text.base;
1395
- _v$3 !== _p$.e && (_p$.e = _$setProp(_el$8, "height", _v$3, _p$.e));
1396
- _v$4 !== _p$.t && (_p$.t = _$setProp(_el$0, "fg", _v$4, _p$.t));
1397
- _v$5 !== _p$.a && (_p$.a = _$setProp(_el$11, "fg", _v$5, _p$.a));
1398
- _v$6 !== _p$.o && (_p$.o = _$setProp(_el$13, "height", _v$6, _p$.o));
1399
- _v$7 !== _p$.i && (_p$.i = _$setProp(_el$14, "fg", _v$7, _p$.i));
1739
+ var _v$8 = contentHeight() + 5, _v$9 = context.theme.text.base, _v$0 = context.theme.text.muted, _v$1 = contentHeight();
1740
+ _v$8 !== _p$.e && (_p$.e = _$setProp(_el$46, "height", _v$8, _p$.e));
1741
+ _v$9 !== _p$.t && (_p$.t = _$setProp(_el$48, "fg", _v$9, _p$.t));
1742
+ _v$0 !== _p$.a && (_p$.a = _$setProp(_el$51, "fg", _v$0, _p$.a));
1743
+ _v$1 !== _p$.o && (_p$.o = _$setProp(_el$53, "height", _v$1, _p$.o));
1400
1744
  return _p$;
1401
1745
  }, {
1402
1746
  e: undefined,
1403
1747
  t: undefined,
1404
1748
  a: undefined,
1405
- o: undefined,
1406
- i: undefined
1749
+ o: undefined
1407
1750
  });
1408
- return _el$8;
1751
+ return _el$46;
1409
1752
  })();
1410
1753
  }
1411
1754
  function showStatus(place = currentPlace()) {