git-stack-cli 1.15.0 → 1.15.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 (49) hide show
  1. package/README.md +2 -4
  2. package/dist/cjs/index.cjs +150 -83
  3. package/package.json +2 -1
  4. package/scripts/link.ts +14 -0
  5. package/src/app/App.tsx +39 -31
  6. package/src/app/AutoUpdate.tsx +9 -24
  7. package/src/app/CherryPickCheck.tsx +1 -2
  8. package/src/app/Debug.tsx +3 -5
  9. package/src/app/DependencyCheck.tsx +6 -6
  10. package/src/app/DetectInitialPR.tsx +2 -8
  11. package/src/app/DirtyCheck.tsx +15 -1
  12. package/src/app/Exit.tsx +1 -5
  13. package/src/app/FormatText.tsx +1 -5
  14. package/src/app/GatherMetadata.tsx +6 -13
  15. package/src/app/GithubApiError.tsx +1 -1
  16. package/src/app/HandleCtrlCSigint.tsx +1 -12
  17. package/src/app/LocalCommitStatus.tsx +1 -3
  18. package/src/app/LogTimestamp.tsx +1 -5
  19. package/src/app/ManualRebase.tsx +4 -8
  20. package/src/app/MultiSelect.tsx +2 -2
  21. package/src/app/PostRebaseStatus.tsx +2 -0
  22. package/src/app/PreManualRebase.tsx +3 -5
  23. package/src/app/RebaseCheck.tsx +1 -2
  24. package/src/app/SelectCommitRanges.tsx +6 -10
  25. package/src/app/Status.tsx +1 -1
  26. package/src/app/StatusTable.tsx +1 -4
  27. package/src/app/Store.tsx +5 -1
  28. package/src/app/SyncGithub.tsx +4 -16
  29. package/src/app/Table.tsx +4 -14
  30. package/src/app/TextInput.tsx +2 -7
  31. package/src/app/VerboseDebugInfo.tsx +1 -5
  32. package/src/app/YesNoPrompt.tsx +42 -31
  33. package/src/command.ts +8 -17
  34. package/src/commands/Fixup.tsx +15 -22
  35. package/src/commands/Log.tsx +3 -7
  36. package/src/commands/Rebase.tsx +6 -8
  37. package/src/components/ErrorBoundary.tsx +79 -0
  38. package/src/components/ExitingGate.tsx +27 -0
  39. package/src/core/CommitMetadata.ts +1 -1
  40. package/src/core/GitReviseTodo.test.ts +3 -3
  41. package/src/core/GitReviseTodo.ts +2 -8
  42. package/src/core/Metadata.test.ts +4 -4
  43. package/src/core/StackSummaryTable.ts +3 -3
  44. package/src/core/chalk.ts +1 -5
  45. package/src/core/cli.ts +2 -2
  46. package/src/core/github.tsx +7 -13
  47. package/src/core/pretty_json.ts +1 -6
  48. package/src/github/gh.auth_status.test.ts +2 -6
  49. package/src/index.tsx +24 -3
@@ -2,56 +2,67 @@ import * as React from "react";
2
2
 
3
3
  import * as Ink from "ink-cjs";
4
4
 
5
+ import { FormatText } from "~/app/FormatText";
5
6
  import { Parens } from "~/app/Parens";
6
7
  import { colors } from "~/core/colors";
7
8
 
9
+ type Handler = () => void;
10
+
8
11
  type Props = {
9
12
  message: React.ReactNode;
10
- onYes(): void;
11
- onNo(): void;
13
+ onYes: Handler;
14
+ onNo: Handler;
12
15
  };
13
16
 
14
17
  export function YesNoPrompt(props: Props) {
15
18
  const [answer, set_answer] = React.useState("");
16
19
 
20
+ const answered_ref = React.useRef(false);
21
+
17
22
  Ink.useInput((input) => {
18
- const inputLower = input.toLowerCase();
23
+ // prevent answering multiple times
24
+ if (answered_ref.current) {
25
+ return;
26
+ }
19
27
 
20
- set_answer(inputLower);
28
+ const input_lower = input.toLowerCase();
21
29
 
22
- switch (inputLower) {
30
+ let handler: undefined | Handler;
31
+
32
+ switch (input_lower) {
23
33
  case "n":
24
- return props.onNo();
34
+ handler = props.onNo;
35
+ break;
25
36
 
26
37
  case "y":
27
- return props.onYes();
38
+ handler = props.onYes;
39
+ break;
40
+ }
41
+
42
+ // handler if valid answer (y or n)
43
+ if (handler) {
44
+ answered_ref.current = true;
45
+ set_answer(input_lower);
46
+ handler();
28
47
  }
29
48
  });
30
49
 
31
- // prettier-ignore
32
- const y = <Ink.Text bold color={colors.green}>Y</Ink.Text>;
33
- const n = <Ink.Text color={colors.red}>n</Ink.Text>;
34
-
35
- let choices;
36
-
37
- switch (answer) {
38
- case "y":
39
- choices = y;
40
- break;
41
-
42
- case "n":
43
- choices = n;
44
- break;
45
-
46
- default:
47
- choices = (
48
- <React.Fragment>
49
- {y}
50
- <Ink.Text>/</Ink.Text>
51
- {n}
52
- </React.Fragment>
53
- );
54
- }
50
+ const choices = (function get_choices() {
51
+ // prettier-ignore
52
+ const y = <Ink.Text bold color={colors.green}>Y</Ink.Text>;
53
+ const n = <Ink.Text color={colors.red}>n</Ink.Text>;
54
+
55
+ switch (answer) {
56
+ case "y":
57
+ return y;
58
+
59
+ case "n":
60
+ return n;
61
+
62
+ default:
63
+ return <FormatText message="{y}/{n}" values={{ y, n }} />;
64
+ }
65
+ })();
55
66
 
56
67
  return (
57
68
  <Ink.Box flexDirection="column">
package/src/command.ts CHANGED
@@ -11,26 +11,22 @@ export async function command() {
11
11
  yargs(hideBin(process.argv))
12
12
  .usage("Usage: git stack [command] [options]")
13
13
 
14
- .command("$0", "Sync commit ranges to Github", (yargs) =>
15
- yargs.options(DefaultOptions)
16
- )
14
+ .command("$0", "Sync commit ranges to Github", (yargs) => yargs.options(DefaultOptions))
17
15
 
18
- .command(
19
- "fixup [commit]",
20
- "Amend staged changes to a specific commit in history",
21
- (yargs) => yargs.positional("commit", FixupOptions.commit)
16
+ .command("fixup [commit]", "Amend staged changes to a specific commit in history", (yargs) =>
17
+ yargs.positional("commit", FixupOptions.commit),
22
18
  )
23
19
 
24
20
  .command(
25
21
  "log [args...]",
26
22
  "Print an abbreviated log with numbered commits, useful for git stack fixup",
27
- (yargs) => yargs.strict(false)
23
+ (yargs) => yargs.strict(false),
28
24
  )
29
25
 
30
26
  .command(
31
27
  "rebase",
32
28
  "Update local branch via rebase with latest changes from origin master branch",
33
- (yargs) => yargs
29
+ (yargs) => yargs,
34
30
  )
35
31
 
36
32
  .option("verbose", GlobalOptions.verbose)
@@ -43,12 +39,8 @@ export async function command() {
43
39
  // disallow unknown options
44
40
  .strict()
45
41
  .version(process.env.CLI_VERSION || "unknown")
46
- .showHidden(
47
- "show-hidden",
48
- "Show hidden options via `git stack help --show-hidden`"
49
- )
50
- .help("help", "Show usage via `git stack help`")
51
- .argv as unknown as Promise<Argv>
42
+ .showHidden("show-hidden", "Show hidden options via `git stack help --show-hidden`")
43
+ .help("help", "Show usage via `git stack help`").argv as unknown as Promise<Argv>
52
44
  );
53
45
  }
54
46
 
@@ -86,8 +78,7 @@ const DefaultOptions = {
86
78
  "verify": {
87
79
  type: "boolean",
88
80
  default: true,
89
- description:
90
- "Run git hooks such as pre-commit and pre-push, disable with --no-verify",
81
+ description: "Run git hooks such as pre-commit and pre-push, disable with --no-verify",
91
82
  },
92
83
 
93
84
  "update": {
@@ -22,31 +22,23 @@ async function run() {
22
22
 
23
23
  if (!relative_number) {
24
24
  actions.output(
25
- <Ink.Text color={colors.red}>
26
- ❗️ Usage: git fixup {"<relative-commit-number>"}
27
- </Ink.Text>
25
+ <Ink.Text color={colors.red}>❗️ Usage: git fixup {"<relative-commit-number>"}</Ink.Text>,
28
26
  );
29
27
  actions.output("");
28
+ actions.output("This script automates the process of adding staged changes as a fixup commit");
30
29
  actions.output(
31
- "This script automates the process of adding staged changes as a fixup commit"
32
- );
33
- actions.output(
34
- "and the subsequent git rebase to flatten the commits based on relative commit number"
35
- );
36
- actions.output(
37
- "You can use a `git log` like below to get the relative commit number"
30
+ "and the subsequent git rebase to flatten the commits based on relative commit number",
38
31
  );
32
+ actions.output("You can use a `git log` like below to get the relative commit number");
39
33
  actions.output("");
40
34
  actions.output(" ❯ git stack log");
41
35
  actions.output(
42
- " 1\te329794d5f881cbf0fc3f26d2108cf6f3fdebabe enable drop_error_subtask test param"
36
+ " 1\te329794d5f881cbf0fc3f26d2108cf6f3fdebabe enable drop_error_subtask test param",
43
37
  );
44
38
  actions.output(
45
- " 2\t57f43b596e5c6b97bc47e2a591f82ccc81651156 test drop_error_subtask baseline"
46
- );
47
- actions.output(
48
- " 3\t838e878d483c6a2d5393063fc59baf2407225c6d ErrorSubtask test baseline"
39
+ " 2\t57f43b596e5c6b97bc47e2a591f82ccc81651156 test drop_error_subtask baseline",
49
40
  );
41
+ actions.output(" 3\t838e878d483c6a2d5393063fc59baf2407225c6d ErrorSubtask test baseline");
50
42
  actions.output("");
51
43
  actions.output("To target `838e87` above, you would call `fixup 3`");
52
44
 
@@ -71,8 +63,7 @@ async function run() {
71
63
  const adjusted_number = Number(relative_number) - 1;
72
64
 
73
65
  // get the commit SHA of the target commit
74
- const commit_sha = (await cli(`git rev-parse HEAD~${adjusted_number}`))
75
- .stdout;
66
+ const commit_sha = (await cli(`git rev-parse HEAD~${adjusted_number}`)).stdout;
76
67
 
77
68
  actions.output(
78
69
  <FormatText
@@ -82,7 +73,7 @@ async function run() {
82
73
  commit_sha: <Parens>{commit_sha}</Parens>,
83
74
  relative_number: relative_number,
84
75
  }}
85
- />
76
+ />,
86
77
  );
87
78
 
88
79
  await cli(`git commit --fixup ${commit_sha}`);
@@ -99,7 +90,11 @@ async function run() {
99
90
 
100
91
  await cli("git stash --include-untracked");
101
92
 
102
- actions.output(<Ink.Text>📦 Changes saved to stash</Ink.Text>);
93
+ actions.output(
94
+ <Ink.Text color={colors.yellow}>
95
+ <FormatText message="📦 Changes saved to stash" />
96
+ </Ink.Text>,
97
+ );
103
98
  }
104
99
 
105
100
  try {
@@ -120,9 +115,7 @@ async function run() {
120
115
  if (save_stash) {
121
116
  await cli("git stash pop");
122
117
 
123
- actions.output(
124
- <Ink.Text color={colors.green}>✅ Changes restored from stash</Ink.Text>
125
- );
118
+ actions.output(<Ink.Text color={colors.green}>✅ Changes restored from stash</Ink.Text>);
126
119
  }
127
120
  }
128
121
  }
@@ -50,13 +50,9 @@ async function run(args: Args) {
50
50
  const subject_format = `%<(60,trunc)%s`;
51
51
 
52
52
  // combine all the above formats into one
53
- const format = [
54
- sha_format,
55
- date_format,
56
- author_format,
57
- decoration_format,
58
- subject_format,
59
- ].join(" ");
53
+ const format = [sha_format, date_format, author_format, decoration_format, subject_format].join(
54
+ " ",
55
+ );
60
56
 
61
57
  // view the SHA, description and history graph of last 20 commits
62
58
  const rest_args = process_argv.slice(3).join(" ");
@@ -54,9 +54,7 @@ Rebase.run = async function run() {
54
54
  await cli(`pwd`);
55
55
 
56
56
  // update local master to match remote
57
- await cli(
58
- `git fetch --no-tags -v origin ${master_branch}:${master_branch}`
59
- );
57
+ await cli(`git fetch --no-tags -v origin ${master_branch}:${master_branch}`);
60
58
 
61
59
  const master_sha = (await cli(`git rev-parse ${master_branch}`)).stdout;
62
60
  const rebase_merge_base = master_sha;
@@ -83,7 +81,7 @@ Rebase.run = async function run() {
83
81
  commit_message: <Brackets>{commit.subject_line}</Brackets>,
84
82
  pr_status: <Parens>MERGED</Parens>,
85
83
  }}
86
- />
84
+ />,
87
85
  );
88
86
  }
89
87
 
@@ -98,7 +96,7 @@ Rebase.run = async function run() {
98
96
  values={{
99
97
  commit_message: <Brackets>{commit.subject_line}</Brackets>,
100
98
  }}
101
- />
99
+ />,
102
100
  );
103
101
  }
104
102
 
@@ -131,7 +129,7 @@ Rebase.run = async function run() {
131
129
  branch_name: <Brackets>{branch_name}</Brackets>,
132
130
  origin_branch: <Brackets>{`origin/${master_branch}`}</Brackets>,
133
131
  }}
134
- />
132
+ />,
135
133
  );
136
134
 
137
135
  actions.unregister_abort_handler();
@@ -182,7 +180,7 @@ Rebase.run = async function run() {
182
180
  actions.output(
183
181
  <Ink.Text color={colors.yellow}>
184
182
  Restoring <Brackets>{branch_name}</Brackets>…
185
- </Ink.Text>
183
+ </Ink.Text>,
186
184
  );
187
185
 
188
186
  restore_git();
@@ -190,7 +188,7 @@ Rebase.run = async function run() {
190
188
  actions.output(
191
189
  <Ink.Text color={colors.yellow}>
192
190
  Restored <Brackets>{branch_name}</Brackets>.
193
- </Ink.Text>
191
+ </Ink.Text>,
194
192
  );
195
193
  }
196
194
  };
@@ -0,0 +1,79 @@
1
+ /* eslint-disable no-console */
2
+ import * as React from "react";
3
+
4
+ import * as Ink from "ink-cjs";
5
+
6
+ import { FormatText } from "~/app/FormatText";
7
+ import { Store } from "~/app/Store";
8
+ import { colors } from "~/core/colors";
9
+
10
+ type Props = {
11
+ children: React.ReactNode;
12
+ };
13
+
14
+ type State = {
15
+ error: null | Error;
16
+ component_stack: string;
17
+ };
18
+
19
+ export class ErrorBoundary extends React.Component<Props, State> {
20
+ constructor(props: Props) {
21
+ super(props);
22
+
23
+ this.state = {
24
+ error: null,
25
+ component_stack: "",
26
+ };
27
+ }
28
+
29
+ static getDerivedStateFromError(error: Error) {
30
+ return { error };
31
+ }
32
+
33
+ override componentDidCatch(_error: Error, error_info: React.ErrorInfo) {
34
+ let component_stack = error_info.componentStack;
35
+
36
+ if (component_stack) {
37
+ // remove first line of component_stack
38
+ component_stack = component_stack.split("\n").slice(1).join("\n");
39
+ this.setState({ component_stack });
40
+ }
41
+ }
42
+
43
+ override render() {
44
+ if (!this.state.error) {
45
+ return this.props.children;
46
+ }
47
+
48
+ const message = this.state.error.message;
49
+
50
+ return (
51
+ <Ink.Box flexDirection="column" gap={0}>
52
+ <Ink.Text color={colors.red}>
53
+ <FormatText
54
+ message="🚨 Unhandled error {message}"
55
+ values={{
56
+ message: <Ink.Text color={colors.gray}>{message}</Ink.Text>,
57
+ }}
58
+ />
59
+ </Ink.Text>
60
+
61
+ {this._render_verbose()}
62
+ </Ink.Box>
63
+ );
64
+ }
65
+
66
+ _render_verbose() {
67
+ const store_state = Store.getState();
68
+
69
+ if (store_state.argv.verbose) {
70
+ return <Ink.Text color={colors.gray}>{this.state.component_stack}</Ink.Text>;
71
+ }
72
+
73
+ return (
74
+ <Ink.Text color={colors.gray}>
75
+ <FormatText message="Try again with `--verbose` to see more information." />
76
+ </Ink.Text>
77
+ );
78
+ }
79
+ }
@@ -0,0 +1,27 @@
1
+ import * as React from "react";
2
+
3
+ import * as Ink from "ink-cjs";
4
+
5
+ import { FormatText } from "~/app/FormatText";
6
+ import { Store } from "~/app/Store";
7
+ import { colors } from "~/core/colors";
8
+
9
+ type Props = {
10
+ children: React.ReactNode;
11
+ };
12
+
13
+ export function ExitingGate(props: Props) {
14
+ const is_exiting = Store.useState((state) => state.is_exiting);
15
+
16
+ if (!is_exiting) {
17
+ return props.children;
18
+ }
19
+
20
+ return (
21
+ <Ink.Box flexDirection="column">
22
+ <Ink.Text color={colors.red}>
23
+ <FormatText message="🚨 Exiting…" />
24
+ </Ink.Text>
25
+ </Ink.Box>
26
+ );
27
+ }
@@ -167,7 +167,7 @@ export async function range(commit_group_map?: CommitGroupMap) {
167
167
  async function get_commit_list() {
168
168
  const master_branch = Store.getState().master_branch;
169
169
  const log_result = await cli(
170
- `git log ${master_branch}..HEAD --oneline --format=%H --color=never`
170
+ `git log ${master_branch}..HEAD --oneline --format=%H --color=never`,
171
171
  );
172
172
 
173
173
  if (!log_result.stdout) {
@@ -35,7 +35,7 @@ test("git-revise-todo from commit range with single new commit", () => {
35
35
  "",
36
36
  "git-stack-id: E63ytp5dj",
37
37
  "git-stack-title: lemon color",
38
- ].join("\n")
38
+ ].join("\n"),
39
39
  );
40
40
  });
41
41
 
@@ -53,7 +53,7 @@ test("git-revise-todo from commit range with single new commit in new group", ()
53
53
  "",
54
54
  "git-stack-id: 6Ak-qn+5Z",
55
55
  "git-stack-title: new group",
56
- ].join("\n")
56
+ ].join("\n"),
57
57
  );
58
58
  });
59
59
 
@@ -71,7 +71,7 @@ test("git-revise-todo handles double quotes in commit message", () => {
71
71
  "",
72
72
  "git-stack-id: 6Ak-qn+5Z",
73
73
  'git-stack-title: [new] invalid \\"by me\\" quotes',
74
- ].join("\n")
74
+ ].join("\n"),
75
75
  );
76
76
  });
77
77
 
@@ -87,10 +87,7 @@ GitReviseTodo.todo = function todo(args: CommitListArgs) {
87
87
 
88
88
  const metadata = { id, title };
89
89
 
90
- const unsafe_message_with_id = Metadata.write(
91
- commit.full_message,
92
- metadata
93
- );
90
+ const unsafe_message_with_id = Metadata.write(commit.full_message, metadata);
94
91
 
95
92
  let message_with_id = unsafe_message_with_id;
96
93
 
@@ -112,10 +109,7 @@ GitReviseTodo.todo = function todo(args: CommitListArgs) {
112
109
 
113
110
  GitReviseTodo.execute = async function grt_execute(args: ExecuteArgs) {
114
111
  // generate temporary directory and drop sequence editor script
115
- const tmp_git_sequence_editor_path = path.join(
116
- os.tmpdir(),
117
- "git-sequence-editor.sh"
118
- );
112
+ const tmp_git_sequence_editor_path = path.join(os.tmpdir(), "git-sequence-editor.sh");
119
113
 
120
114
  // replaced at build time with literal contents of `scripts/git-sequence-editor.sh`
121
115
  const GIT_SEQUENCE_EDITOR_SCRIPT = `process.env.GIT_SEQUENCE_EDITOR_SCRIPT`;
@@ -48,7 +48,7 @@ test("write handles bulleted lists", () => {
48
48
  "",
49
49
  "git-stack-id: abcd1234",
50
50
  "git-stack-title: banana",
51
- ].join("\n")
51
+ ].join("\n"),
52
52
  );
53
53
  });
54
54
 
@@ -94,7 +94,7 @@ test("write handles bulleted lists", () => {
94
94
  "",
95
95
  "git-stack-id: fix-slash-branch",
96
96
  "git-stack-title: fix slash branch",
97
- ].join("\n")
97
+ ].join("\n"),
98
98
  );
99
99
  });
100
100
 
@@ -133,7 +133,7 @@ test("write handles double quotes", () => {
133
133
  "",
134
134
  "git-stack-id: abc123",
135
135
  'git-stack-title: Revert \\"[abc / 123] subject (#1234)\\"',
136
- ].join("\n")
136
+ ].join("\n"),
137
137
  );
138
138
  });
139
139
 
@@ -156,6 +156,6 @@ test("removes metadata", () => {
156
156
  "- keyboard modality escape key",
157
157
  "- centralize settings",
158
158
  "- move logic inside if branch",
159
- ].join("\n")
159
+ ].join("\n"),
160
160
  );
161
161
  });
@@ -122,7 +122,7 @@ const TEMPLATE = {
122
122
  const RE = {
123
123
  // https://regex101.com/r/kqB9Ft/1
124
124
  stack_table_legacy: new RegExp(
125
- TEMPLATE.stack_table_legacy("\\s+(?<rows>(?:- [^\r^\n]*(?:[\r\n]+)?)+)")
125
+ TEMPLATE.stack_table_legacy("\\s+(?<rows>(?:- [^\r^\n]*(?:[\r\n]+)?)+)"),
126
126
  ),
127
127
 
128
128
  stack_table_link: new RegExp(
@@ -131,7 +131,7 @@ const RE = {
131
131
  .replace("]", "\\]")
132
132
  .replace("(", "\\(")
133
133
  .replace(")", "\\)")
134
- .replace("ROWS", "\\s+(?<rows>(?:- [^\r^\n]*(?:[\r\n]+)?)+)")
134
+ .replace("ROWS", "\\s+(?<rows>(?:- [^\r^\n]*(?:[\r\n]+)?)+)"),
135
135
  ),
136
136
 
137
137
  row: new RegExp(
@@ -139,7 +139,7 @@ const RE = {
139
139
  icon: "(?<icon>.+)",
140
140
  num: "(?<num>\\d+)",
141
141
  pr_url: "(?<pr_url>.+)",
142
- })
142
+ }),
143
143
  ),
144
144
 
145
145
  pr_url: /^https:\/\/.*$/,
package/src/core/chalk.ts CHANGED
@@ -19,11 +19,7 @@ function create_color_proxy(base: typeof chalk): ColorProxy {
19
19
 
20
20
  case "bracket":
21
21
  return (str: string) =>
22
- [
23
- target.bold.whiteBright("["),
24
- str,
25
- target.bold.whiteBright("]"),
26
- ].join("");
22
+ [target.bold.whiteBright("["), str, target.bold.whiteBright("]")].join("");
27
23
 
28
24
  case "url":
29
25
  return target.bold.underline.blueBright;
package/src/core/cli.ts CHANGED
@@ -22,7 +22,7 @@ let i = 0;
22
22
 
23
23
  export async function cli(
24
24
  unsafe_command: string | Array<string | number>,
25
- unsafe_options?: Options
25
+ unsafe_options?: Options,
26
26
  ): Promise<Return> {
27
27
  const options = Object.assign({}, unsafe_options);
28
28
 
@@ -97,7 +97,7 @@ export async function cli(
97
97
 
98
98
  cli.sync = function cli_sync(
99
99
  unsafe_command: string | Array<string | number>,
100
- unsafe_options?: Options
100
+ unsafe_options?: Options,
101
101
  ): Return {
102
102
  const options = Object.assign({}, unsafe_options);
103
103
 
@@ -24,7 +24,7 @@ export async function pr_list(): Promise<Array<PullRequest>> {
24
24
  invariant(repo_path, "repo_path must exist");
25
25
 
26
26
  const result_pr_list = await gh_json<Array<PullRequest>>(
27
- `pr list --repo ${repo_path} --author ${username} --state open ${JSON_FIELDS}`
27
+ `pr list --repo ${repo_path} --author ${username} --state open ${JSON_FIELDS}`,
28
28
  );
29
29
 
30
30
  if (result_pr_list instanceof Error) {
@@ -42,7 +42,7 @@ export async function pr_list(): Promise<Array<PullRequest>> {
42
42
  <Brackets>{repo_path}</Brackets>
43
43
  <Ink.Text>{" authored by "}</Ink.Text>
44
44
  <Brackets>{username}</Brackets>
45
- </Ink.Text>
45
+ </Ink.Text>,
46
46
  );
47
47
  }
48
48
 
@@ -77,7 +77,7 @@ export async function pr_status(branch: string): Promise<null | PullRequest> {
77
77
  </Ink.Text>
78
78
  <Ink.Text> </Ink.Text>
79
79
  <Ink.Text dimColor>{branch}</Ink.Text>
80
- </Ink.Text>
80
+ </Ink.Text>,
81
81
  );
82
82
  }
83
83
 
@@ -94,13 +94,11 @@ export async function pr_status(branch: string): Promise<null | PullRequest> {
94
94
  </Ink.Text>
95
95
  <Ink.Text> </Ink.Text>
96
96
  <Ink.Text dimColor>{branch}</Ink.Text>
97
- </Ink.Text>
97
+ </Ink.Text>,
98
98
  );
99
99
  }
100
100
 
101
- const pr = await gh_json<PullRequest>(
102
- `pr view ${branch} --repo ${repo_path} ${JSON_FIELDS}`
103
- );
101
+ const pr = await gh_json<PullRequest>(`pr view ${branch} --repo ${repo_path} ${JSON_FIELDS}`);
104
102
 
105
103
  if (pr instanceof Error) {
106
104
  return null;
@@ -190,9 +188,7 @@ export async function pr_draft(args: DraftPullRequestArgs) {
190
188
  // https://docs.github.com/en/graphql/reference/mutations#convertpullrequesttodraft
191
189
  // https://docs.github.com/en/graphql/reference/mutations#markpullrequestreadyforreview
192
190
 
193
- const mutation_name = args.draft
194
- ? "convertPullRequestToDraft"
195
- : "markPullRequestReadyForReview";
191
+ const mutation_name = args.draft ? "convertPullRequestToDraft" : "markPullRequestReadyForReview";
196
192
 
197
193
  let query = `
198
194
  mutation($id: ID!) {
@@ -215,9 +211,7 @@ export async function pr_draft(args: DraftPullRequestArgs) {
215
211
  const cache_pr = state.pr[args.branch];
216
212
  invariant(cache_pr, "cache_pr must exist");
217
213
 
218
- const command_parts = [
219
- `gh api graphql -F id="${cache_pr.id}" -f query='${query}'`,
220
- ];
214
+ const command_parts = [`gh api graphql -F id="${cache_pr.id}" -f query='${query}'`];
221
215
 
222
216
  const command = command_parts.join(" ");
223
217
 
@@ -1,10 +1,5 @@
1
1
  export namespace pretty_json {
2
- export type JSONValue =
3
- | null
4
- | number
5
- | string
6
- | boolean
7
- | { [key: string]: JSONValue };
2
+ export type JSONValue = null | number | string | boolean | { [key: string]: JSONValue };
8
3
  }
9
4
 
10
5
  export function pretty_json<T extends pretty_json.JSONValue>(input: T): string {
@@ -3,17 +3,13 @@ import { test, expect } from "bun:test";
3
3
  import * as gh from "./gh";
4
4
 
5
5
  test("logged in as", () => {
6
- const username = gh.auth_status(
7
- " ✓ Logged in to github.com as magus (keyring)\n"
8
- );
6
+ const username = gh.auth_status(" ✓ Logged in to github.com as magus (keyring)\n");
9
7
 
10
8
  expect(username).toBe("magus");
11
9
  });
12
10
 
13
11
  test("logged in without as", () => {
14
- const username = gh.auth_status(
15
- "✓ Logged in to github.com account xoxohorses (keyring)"
16
- );
12
+ const username = gh.auth_status("✓ Logged in to github.com account xoxohorses (keyring)");
17
13
 
18
14
  expect(username).toBe("xoxohorses");
19
15
  });