clairo 1.0.2 → 1.0.4

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 (2) hide show
  1. package/dist/cli.js +77 -11
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -125,6 +125,33 @@ function getCurrentBranch() {
125
125
  return { success: false, error: "Failed to get current branch" };
126
126
  }
127
127
  }
128
+ function findRemoteWithBranch(branch) {
129
+ try {
130
+ const remoteBranches = execSync("git branch -r", {
131
+ encoding: "utf-8",
132
+ stdio: ["pipe", "pipe", "pipe"]
133
+ });
134
+ const remotes = listRemotes();
135
+ if (!remotes.success) {
136
+ return { success: false, error: "Failed to list remotes" };
137
+ }
138
+ for (const remote of remotes.data) {
139
+ if (remoteBranches.includes(`${remote.name}/${branch}`)) {
140
+ const sshMatch = remote.url.match(/git@github\.com:(.+?)\/(.+?)(?:\.git)?$/);
141
+ if (sshMatch) {
142
+ return { success: true, data: { remote: remote.name, owner: sshMatch[1] } };
143
+ }
144
+ const httpsMatch = remote.url.match(/https:\/\/github\.com\/(.+?)\/(.+?)(?:\.git)?$/);
145
+ if (httpsMatch) {
146
+ return { success: true, data: { remote: remote.name, owner: httpsMatch[1] } };
147
+ }
148
+ }
149
+ }
150
+ return { success: false, error: "Branch not found on any remote" };
151
+ } catch {
152
+ return { success: false, error: "Failed to find remote with branch" };
153
+ }
154
+ }
128
155
 
129
156
  // src/lib/github/index.ts
130
157
  import { exec } from "child_process";
@@ -730,6 +757,15 @@ function getCheckIcon(check) {
730
757
  if (check.status === "COMPLETED") return "\u2713";
731
758
  return "?";
732
759
  }
760
+ function getCheckSortOrder(check) {
761
+ const conclusion = check.conclusion ?? check.state;
762
+ if (conclusion === "FAILURE" || conclusion === "ERROR") return 0;
763
+ if (conclusion === "PENDING" || check.status === "IN_PROGRESS" || check.status === "QUEUED" || check.status === "WAITING")
764
+ return 1;
765
+ if (conclusion === "SKIPPED" || conclusion === "NEUTRAL") return 2;
766
+ if (conclusion === "SUCCESS" || check.status === "COMPLETED") return 3;
767
+ return 4;
768
+ }
733
769
  function PRDetailsBox({ pr, loading, error, isFocused }) {
734
770
  var _a, _b, _c, _d, _e, _f, _g;
735
771
  const scrollRef = useRef(null);
@@ -824,12 +860,25 @@ function PRDetailsBox({ pr, loading, error, isFocused }) {
824
860
  ] }),
825
861
  (((_f = pr.statusCheckRollup) == null ? void 0 : _f.length) ?? 0) > 0 && /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, flexDirection: "column", children: [
826
862
  /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "Checks:" }),
827
- (_g = pr.statusCheckRollup) == null ? void 0 : _g.map((check, idx) => /* @__PURE__ */ jsxs2(Text2, { color: getCheckColor(check), children: [
828
- " ",
829
- getCheckIcon(check),
830
- " ",
831
- check.name ?? check.context
832
- ] }, idx))
863
+ Array.from(
864
+ ((_g = pr.statusCheckRollup) == null ? void 0 : _g.reduce((acc, check) => {
865
+ const key = check.name ?? check.context ?? "";
866
+ const existing = acc.get(key);
867
+ if (!existing || (check.startedAt ?? "") > (existing.startedAt ?? "")) {
868
+ acc.set(key, check);
869
+ }
870
+ return acc;
871
+ }, /* @__PURE__ */ new Map()).values()) ?? []
872
+ ).sort((a, b) => getCheckSortOrder(a) - getCheckSortOrder(b)).map((check, idx) => {
873
+ const jobName = check.name ?? check.context;
874
+ const displayName = check.workflowName ? `${check.workflowName} / ${jobName}` : jobName;
875
+ return /* @__PURE__ */ jsxs2(Text2, { color: getCheckColor(check), children: [
876
+ " ",
877
+ getCheckIcon(check),
878
+ " ",
879
+ displayName
880
+ ] }, idx);
881
+ })
833
882
  ] }),
834
883
  pr.body && /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, flexDirection: "column", children: [
835
884
  /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "Description:" }),
@@ -896,7 +945,7 @@ function PullRequestsBox({
896
945
  if (key.downArrow || input === "j") {
897
946
  setHighlightedIndex((prev) => Math.min(totalItems - 1, prev + 1));
898
947
  }
899
- if (key.return) {
948
+ if (input === " ") {
900
949
  if (highlightedIndex === prs.length) {
901
950
  onCreatePR();
902
951
  } else if (prs[highlightedIndex]) {
@@ -967,7 +1016,7 @@ function RemotesBox({ remotes, selectedRemote, onSelect, loading, error, isFocus
967
1016
  if (key.downArrow || input === "j") {
968
1017
  setHighlightedIndex((prev) => Math.min(remotes.length - 1, prev + 1));
969
1018
  }
970
- if (key.return) {
1019
+ if (input === " ") {
971
1020
  onSelect(remotes[highlightedIndex].name);
972
1021
  }
973
1022
  },
@@ -1027,8 +1076,9 @@ function GitHubView({ isFocused, onKeybindingsChange, onLogUpdated }) {
1027
1076
  }
1028
1077
  const bindings = [];
1029
1078
  if (focusedBox === "remotes") {
1030
- bindings.push({ key: "Enter", label: "Select Remote" });
1079
+ bindings.push({ key: "Space", label: "Select Remote" });
1031
1080
  } else if (focusedBox === "prs") {
1081
+ bindings.push({ key: "Space", label: "Select" });
1032
1082
  bindings.push({ key: "n", label: "New PR", color: "green" });
1033
1083
  bindings.push({ key: "r", label: "Refresh" });
1034
1084
  bindings.push({ key: "o", label: "Open", color: "green" });
@@ -1140,11 +1190,24 @@ function GitHubView({ isFocused, onKeybindingsChange, onLogUpdated }) {
1140
1190
  const prNumbersBeforeCreate = useRef2(/* @__PURE__ */ new Set());
1141
1191
  const pollingIntervalRef = useRef2(null);
1142
1192
  const handleCreatePR = useCallback(() => {
1193
+ if (!currentBranch) {
1194
+ setErrors((prev) => ({ ...prev, prs: "No branch detected" }));
1195
+ return;
1196
+ }
1197
+ const remoteResult = findRemoteWithBranch(currentBranch);
1198
+ if (!remoteResult.success) {
1199
+ setErrors((prev) => ({ ...prev, prs: "Push your branch to a remote first" }));
1200
+ return;
1201
+ }
1143
1202
  prNumbersBeforeCreate.current = new Set(prs.map((pr) => pr.number));
1144
- exec3("gh pr create --web", () => {
1203
+ const headFlag = `${remoteResult.data.owner}:${currentBranch}`;
1204
+ exec3(`gh pr create --web --head "${headFlag}"`, (error) => {
1145
1205
  process.stdout.emit("resize");
1206
+ if (error) {
1207
+ setErrors((prev) => ({ ...prev, prs: `Failed to create PR: ${error.message}` }));
1208
+ }
1146
1209
  });
1147
- if (!currentBranch || !currentRepoSlug) return;
1210
+ if (!currentRepoSlug) return;
1148
1211
  let attempts = 0;
1149
1212
  const maxAttempts = 24;
1150
1213
  const pollInterval = 5e3;
@@ -1193,6 +1256,9 @@ function GitHubView({ isFocused, onKeybindingsChange, onLogUpdated }) {
1193
1256
  if (focusedBox === "prs") refreshPRs();
1194
1257
  if (focusedBox === "details") refreshDetails();
1195
1258
  }
1259
+ if (input === "n" && focusedBox === "prs") {
1260
+ handleCreatePR();
1261
+ }
1196
1262
  },
1197
1263
  { isActive: isFocused }
1198
1264
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clairo",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",