@solaqua/gji 0.8.0 → 0.9.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.
Files changed (53) hide show
  1. package/README.md +24 -30
  2. package/dist/browser.d.ts +7 -0
  3. package/dist/browser.js +37 -0
  4. package/dist/clean.js +34 -19
  5. package/dist/cli.d.ts +2 -0
  6. package/dist/cli.js +59 -4
  7. package/dist/doctor.d.ts +28 -0
  8. package/dist/doctor.js +454 -0
  9. package/dist/gji-bundle.mjs +2514 -378
  10. package/dist/go.d.ts +2 -1
  11. package/dist/go.js +4 -1
  12. package/dist/index.js +12 -6
  13. package/dist/init.d.ts +15 -0
  14. package/dist/init.js +232 -16
  15. package/dist/open.d.ts +2 -1
  16. package/dist/open.js +2 -1
  17. package/dist/pr-open.d.ts +19 -0
  18. package/dist/pr-open.js +290 -0
  19. package/dist/pull-requests.d.ts +33 -0
  20. package/dist/pull-requests.js +384 -0
  21. package/dist/remove.js +2 -2
  22. package/dist/repo-registry.d.ts +5 -0
  23. package/dist/repo-registry.js +87 -14
  24. package/dist/shell-completion.js +278 -10
  25. package/dist/shell-setup.d.ts +9 -0
  26. package/dist/shell-setup.js +56 -0
  27. package/dist/warp.d.ts +3 -0
  28. package/dist/warp.js +8 -3
  29. package/dist/worktree-management.d.ts +2 -1
  30. package/dist/worktree-management.js +23 -7
  31. package/dist/worktree-picker.d.ts +12 -1
  32. package/dist/worktree-picker.js +84 -6
  33. package/man/man1/gji-back.1 +1 -1
  34. package/man/man1/gji-clean.1 +1 -1
  35. package/man/man1/gji-completion.1 +1 -1
  36. package/man/man1/gji-config.1 +1 -1
  37. package/man/man1/gji-doctor.1 +19 -0
  38. package/man/man1/gji-go.1 +1 -1
  39. package/man/man1/gji-history.1 +1 -1
  40. package/man/man1/gji-init.1 +6 -3
  41. package/man/man1/gji-ls.1 +1 -1
  42. package/man/man1/gji-new.1 +1 -1
  43. package/man/man1/gji-open.1 +1 -1
  44. package/man/man1/gji-pr.1 +5 -1
  45. package/man/man1/gji-remove.1 +1 -1
  46. package/man/man1/gji-root.1 +1 -1
  47. package/man/man1/gji-run-hook.1 +1 -1
  48. package/man/man1/gji-status.1 +1 -1
  49. package/man/man1/gji-sync-files.1 +1 -1
  50. package/man/man1/gji-sync.1 +1 -1
  51. package/man/man1/gji-warp.1 +1 -1
  52. package/man/man1/gji.1 +6 -2
  53. package/package.json +1 -1
package/README.md CHANGED
@@ -9,6 +9,7 @@ That matters even more in AI-assisted workflows, where one repository often has
9
9
  ```sh
10
10
  gji new feature/payment-refactor # new branch + worktree, cd in
11
11
  gji pr 1234 # review PR in isolation, cd in
12
+ gji pr open # open the PR for the current worktree
12
13
  gji go main # jump back, shell changes directory
13
14
  gji remove feature/payment-refactor
14
15
  ```
@@ -69,36 +70,18 @@ That makes Git worktrees more important, because a single shared checkout become
69
70
  npm install -g @solaqua/gji
70
71
  ```
71
72
 
72
- Then add shell integration so `gji go`, `gji new`, and `gji remove` can change your directory:
73
+ Then run the guided setup in an interactive terminal:
73
74
 
74
75
  ```sh
75
- # zsh
76
- echo 'eval "$(gji init zsh)"' >> ~/.zshrc
77
-
78
- # bash
79
- echo 'eval "$(gji init bash)"' >> ~/.bashrc
80
-
81
- # fish
82
- gji init fish --write
83
- source ~/.config/fish/config.fish
76
+ gji init
77
+ # restart your shell, or source the rc file shown by the wizard
78
+ gji doctor
84
79
  ```
85
80
 
86
- Install completions as separate files:
81
+ The wizard installs shell integration, completions, and an available editor. In a non-interactive environment, use the existing explicit shell command instead:
87
82
 
88
83
  ```sh
89
- # zsh
90
- mkdir -p ~/.zsh/completions
91
- gji completion zsh > ~/.zsh/completions/_gji
92
- # add this before running compinit in ~/.zshrc
93
- fpath=(~/.zsh/completions $fpath)
94
-
95
- # bash
96
- mkdir -p ~/.local/share/bash-completion/completions
97
- gji completion bash > ~/.local/share/bash-completion/completions/gji
98
-
99
- # fish
100
- mkdir -p ~/.config/fish/completions
101
- gji completion fish > ~/.config/fish/completions/gji.fish
84
+ gji init zsh --write
102
85
  ```
103
86
 
104
87
  ## Quick start
@@ -112,6 +95,10 @@ gji new feature/dark-mode --open --editor cursor
112
95
 
113
96
  # review a pull request
114
97
  gji pr 1234
98
+ gji pr open # open the PR for the current worktree
99
+ gji pr open --select # choose a PR from any linked worktree
100
+ gji pr open feature/auth-refactor # open the PR for a branch
101
+ gji pr open '#1234' # open an open PR directly
115
102
 
116
103
  # see what's open
117
104
  gji status
@@ -178,13 +165,14 @@ See the full comparison in [website/docs/comparison.mdx](./website/docs/comparis
178
165
 
179
166
  ## Shell setup
180
167
 
181
- Without shell integration `gji` prints paths and exits — which is fine for scripts but means it cannot `cd` you into a new worktree. Install the integration once:
168
+ Without shell integration `gji` prints paths and exits — which is fine for scripts but means it cannot `cd` you into a new worktree. Install the integration, completions, and an editor once with:
182
169
 
183
170
  ```sh
184
- gji init zsh # prints the shell function, review it if you like
171
+ gji init
172
+ gji doctor
185
173
  ```
186
174
 
187
- Install the wrapper once:
175
+ `gji init` is interactive. For dotfiles or CI, the explicit shell commands remain available and preserve their existing output:
188
176
 
189
177
  ```sh
190
178
  # zsh
@@ -243,6 +231,7 @@ path=$(gji root --print)
243
231
  |---|---|
244
232
  | `gji new [branch] [--detached] [--open] [--editor <cli>] [--json]` | create branch + worktree, cd in (validates branch name against Git rules) |
245
233
  | `gji pr <ref> [--json]` | fetch PR ref, create worktree, cd in |
234
+ | `gji pr open [branch|#N] [--select]` | open the current worktree PR, or choose a linked worktree with `--select` |
246
235
  | `gji open [branch] [--editor <cli>] [--save] [--workspace]` | open a worktree in an editor |
247
236
  | `gji go [branch] [--print]` | jump to a worktree |
248
237
  | `gji root [--print]` | jump to the main repo root |
@@ -254,7 +243,8 @@ path=$(gji root --print)
254
243
  | `gji remove [branch] [--force] [--json]` | remove a worktree and its branch |
255
244
  | `gji trigger-hook <hook>` | run a hook in the current worktree |
256
245
  | `gji config [get\|set\|unset] [key] [value]` | manage global defaults |
257
- | `gji init [shell]` | print or install shell integration |
246
+ | `gji init [shell]` | interactively set up onboarding, or print/install a shell wrapper |
247
+ | `gji doctor [--json] [--fix] [--yes]` | check installation and configuration health; optionally remove stale repository entries |
258
248
  | `gji completion [shell]` | print shell completion definitions |
259
249
 
260
250
  ## Configuration
@@ -275,7 +265,7 @@ No setup required. Optional config lives in:
275
265
  | `syncDefaultBranch` | branch to rebase onto (default: remote `HEAD`) |
276
266
  | `syncFiles` | files to copy from main worktree into each new worktree; use global per-repo config for private files |
277
267
  | `skipInstallPrompt` | `true` to disable the auto-install prompt permanently |
278
- | `installSaveTarget` | `"local"` or `"global"` — where **Always**/**Never** choices are persisted (default: `"local"`); set once during `gji init --write` |
268
+ | `installSaveTarget` | `"local"` or `"global"` — where **Always**/**Never** choices are persisted (default: `"local"`); set during `gji init <shell> --write` |
279
269
  | `hooks` | lifecycle scripts (see [Hooks](#hooks)) |
280
270
  | `repos` | per-repo overrides inside the global config (see below) |
281
271
 
@@ -477,7 +467,10 @@ GJI_NO_TUI=1 gji remove --force feature/ci-branch
477
467
  GJI_NO_TUI=1 gji clean --force
478
468
  ```
479
469
 
480
- `GJI_NO_TUI=1` disables all prompts. Commands that need confirmation require `--force`. `--json` implies the same behaviour.
470
+ `GJI_NO_TUI=1` disables all prompts. Commands that need confirmation require their non-interactive approval flag (`--force` for cleanup commands, `--yes` for `doctor --fix`). `--json` implies the same behaviour.
471
+
472
+ `gji pr open --select` requires an interactive terminal; plain `gji pr open` opens the PR for the current worktree without prompting.
473
+ If the current worktree has no open PR or has multiple open PRs in headless mode, pass `gji pr open <branch|#N>` explicitly.
481
474
 
482
475
  Update notifications are also suppressed automatically in non-interactive and `--json` runs. Users can opt out explicitly with `NO_UPDATE_NOTIFIER=1` or `--no-update-notifier`.
483
476
 
@@ -486,6 +479,7 @@ Update notifications are also suppressed automatically in non-interactive and `-
486
479
  - Works from either the main repo root or inside any linked worktree
487
480
  - The current worktree is never offered as a `gji clean` candidate
488
481
  - `gji pr` fetches from `origin` using the first matching forge ref namespace: GitHub `refs/pull/<number>/head`, GitLab `refs/merge-requests/<number>/head`, then Bitbucket `refs/pull-requests/<number>/from`
482
+ - `gji pr open` reads open PRs from the `origin` forge (GitHub, GitLab, or Bitbucket), preferring an installed authenticated provider CLI and falling back to its public API
489
483
 
490
484
  ## License
491
485
 
@@ -0,0 +1,7 @@
1
+ export type BrowserCommandRunner = (command: string, args: string[]) => Promise<void>;
2
+ export interface BrowserOpenerDependencies {
3
+ platform?: NodeJS.Platform;
4
+ runCommand?: BrowserCommandRunner;
5
+ }
6
+ export declare function createBrowserOpener(dependencies?: BrowserOpenerDependencies): (url: string) => Promise<void>;
7
+ export declare const openBrowser: (url: string) => Promise<void>;
@@ -0,0 +1,37 @@
1
+ import { execFile } from "node:child_process";
2
+ import { platform } from "node:process";
3
+ import { promisify } from "node:util";
4
+ const execFileAsync = promisify(execFile);
5
+ export function createBrowserOpener(dependencies = {}) {
6
+ const currentPlatform = dependencies.platform ?? platform;
7
+ const runCommand = dependencies.runCommand ?? defaultRunCommand;
8
+ return async function openBrowser(url) {
9
+ validateBrowserUrl(url);
10
+ const command = browserCommand(currentPlatform, url);
11
+ await runCommand(command.command, command.args);
12
+ };
13
+ }
14
+ export const openBrowser = createBrowserOpener();
15
+ function browserCommand(currentPlatform, url) {
16
+ if (currentPlatform === "darwin")
17
+ return { args: [url], command: "open" };
18
+ if (currentPlatform === "win32") {
19
+ return { args: [url], command: "explorer.exe" };
20
+ }
21
+ return { args: [url], command: "xdg-open" };
22
+ }
23
+ function validateBrowserUrl(url) {
24
+ let parsed;
25
+ try {
26
+ parsed = new URL(url);
27
+ }
28
+ catch {
29
+ throw new Error("browser URL is invalid");
30
+ }
31
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
32
+ throw new Error("browser URL must use http or https");
33
+ }
34
+ }
35
+ async function defaultRunCommand(command, args) {
36
+ await execFileAsync(command, args, { timeout: 10_000 });
37
+ }
package/dist/clean.js CHANGED
@@ -3,7 +3,7 @@ import { loadEffectiveConfig } from "./config.js";
3
3
  import { isBranchMergedInto, readWorktreeHealth, resolveRemoteDefaultBranch, runGit, } from "./git.js";
4
4
  import { isHeadless } from "./headless.js";
5
5
  import { formatLastCommit, formatUpstreamState, readWorktreeInfos, serializeWorktreeInfo, } from "./worktree-info.js";
6
- import { deleteBranch, forceDeleteBranch, forceRemoveWorktree, isBranchUnmergedError, isWorktreeDirtyError, loadLinkedWorktrees, removeWorktree, } from "./worktree-management.js";
6
+ import { deleteBranch, forceDeleteBranch, forceRemoveWorktree, isBranchUnmergedError, isWorktreeDeletionError, isWorktreeForceRemovalError, loadLinkedWorktrees, removeWorktree, } from "./worktree-management.js";
7
7
  import { buildWorktreePromptEntries, promptForMultipleWorktrees, } from "./worktree-picker.js";
8
8
  import { defaultConfirmForceDeleteBranch, defaultConfirmForceRemoveWorktree, } from "./worktree-prompts.js";
9
9
  export function createCleanCommand(dependencies = {}) {
@@ -77,8 +77,8 @@ export function createCleanCommand(dependencies = {}) {
77
77
  }
78
78
  return 0;
79
79
  }
80
- const removedPaths = [];
81
80
  const removedWorktrees = [];
81
+ const failures = [];
82
82
  for (const worktree of selectedWorktrees) {
83
83
  if (options.stale &&
84
84
  !(await isStaleCleanupCandidate(repository.repoRoot, worktree, staleBaseRef))) {
@@ -89,31 +89,39 @@ export function createCleanCommand(dependencies = {}) {
89
89
  await removeWorktree(repository.repoRoot, worktree.path);
90
90
  }
91
91
  catch (error) {
92
- if (!isWorktreeDirtyError(error)) {
93
- throw error;
92
+ if (!isWorktreeForceRemovalError(error)) {
93
+ failures.push({
94
+ branch: worktree.branch,
95
+ message: toMessage(error),
96
+ path: worktree.path,
97
+ });
98
+ continue;
94
99
  }
95
- if (options.stale) {
100
+ if (options.stale && !isWorktreeDeletionError(error)) {
96
101
  options.stderr(`Skipped ${worktree.path}: no longer a safe stale cleanup candidate\n`);
97
102
  continue;
98
103
  }
99
104
  if (!options.force &&
100
105
  !(await confirmForceRemoveWorktree(worktree.path))) {
101
- reportRemovedPaths(removedPaths, options.stderr);
102
- options.stderr("Aborted\n");
103
- return 1;
106
+ failures.push({
107
+ branch: worktree.branch,
108
+ message: "force removal declined",
109
+ path: worktree.path,
110
+ });
111
+ continue;
104
112
  }
105
113
  try {
106
114
  await forceRemoveWorktree(repository.repoRoot, worktree.path);
107
115
  }
108
116
  catch (forceError) {
109
- if (!options.json) {
110
- reportRemovedPaths(removedPaths, options.stderr);
111
- }
112
- emitError(options, `Failed to remove worktree at ${worktree.path}: ${toMessage(forceError)}`);
113
- return 1;
117
+ failures.push({
118
+ branch: worktree.branch,
119
+ message: toMessage(forceError),
120
+ path: worktree.path,
121
+ });
122
+ continue;
114
123
  }
115
124
  }
116
- removedPaths.push(worktree.path);
117
125
  removedWorktrees.push(worktree);
118
126
  if (worktree.branch) {
119
127
  try {
@@ -145,12 +153,16 @@ export function createCleanCommand(dependencies = {}) {
145
153
  ? { branch: worktree.branch, path: worktree.path }
146
154
  : serializeWorktreeInfo(info);
147
155
  });
148
- options.stdout(`${JSON.stringify({ removed }, null, 2)}\n`);
156
+ const payload = failures.length === 0 ? { removed } : { removed, failed: failures };
157
+ options.stdout(`${JSON.stringify(payload, null, 2)}\n`);
158
+ }
159
+ else if (failures.length > 0) {
160
+ reportCleanFailures(failures, options.stderr);
149
161
  }
150
162
  else {
151
163
  options.stdout(`${repository.repoRoot}\n`);
152
164
  }
153
- return 0;
165
+ return failures.length === 0 ? 0 : 1;
154
166
  };
155
167
  }
156
168
  export const runCleanCommand = createCleanCommand();
@@ -216,9 +228,12 @@ function resolveSelectedWorktrees(worktrees, selections) {
216
228
  }
217
229
  return selectedWorktrees;
218
230
  }
219
- function reportRemovedPaths(paths, stderr) {
220
- if (paths.length > 0) {
221
- stderr(`Already removed: ${paths.join(", ")}\n`);
231
+ function reportCleanFailures(failures, stderr) {
232
+ const noun = failures.length === 1 ? "worktree" : "worktrees";
233
+ stderr(`Failed to clean ${failures.length} ${noun}:\n`);
234
+ for (const failure of failures) {
235
+ const branch = failure.branch === null ? "detached" : failure.branch;
236
+ stderr(`- ${failure.path} (${branch}): ${failure.message}\n`);
222
237
  }
223
238
  }
224
239
  function formatCleanInfo(info) {
package/dist/cli.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { Command } from "commander";
2
+ import { type PrOpenCommandDependencies } from "./pr-open.js";
2
3
  export interface RunCliOptions {
3
4
  cwd?: string;
5
+ prOpenDependencies?: Partial<PrOpenCommandDependencies>;
4
6
  stderr?: (chunk: string) => void;
5
7
  stdout?: (chunk: string) => void;
6
8
  }
package/dist/cli.js CHANGED
@@ -5,6 +5,7 @@ import { runBackCommand } from "./back.js";
5
5
  import { runCleanCommand } from "./clean.js";
6
6
  import { runCompletionCommand } from "./completion.js";
7
7
  import { runConfigCommand } from "./config-command.js";
8
+ import { runDoctorCommand } from "./doctor.js";
8
9
  import { runGoCommand } from "./go.js";
9
10
  import { isHeadless } from "./headless.js";
10
11
  import { runHistoryCommand } from "./history-command.js";
@@ -13,6 +14,7 @@ import { runLsCommand } from "./ls.js";
13
14
  import { runNewCommand } from "./new.js";
14
15
  import { runOpenCommand } from "./open.js";
15
16
  import { runPrCommand } from "./pr.js";
17
+ import { createPrOpenCommand, } from "./pr-open.js";
16
18
  import { runRemoveCommand } from "./remove.js";
17
19
  import { detectRepository } from "./repo.js";
18
20
  import { registerRepo } from "./repo-registry.js";
@@ -44,7 +46,9 @@ function readPackageMetadata() {
44
46
  }
45
47
  export async function runCli(argv, options = {}) {
46
48
  await maybeNotifyForUpdates(argv);
47
- maybeRegisterCurrentRepo(options.cwd ?? process.cwd());
49
+ if (argv[0] !== "doctor") {
50
+ maybeRegisterCurrentRepo(options.cwd ?? process.cwd());
51
+ }
48
52
  const program = createProgram();
49
53
  const cwd = options.cwd ?? process.cwd();
50
54
  const stdout = options.stdout ?? (() => undefined);
@@ -59,7 +63,12 @@ export async function runCli(argv, options = {}) {
59
63
  return { exitCode: 0 };
60
64
  }
61
65
  try {
62
- attachCommandActions(program, { cwd, stderr, stdout });
66
+ attachCommandActions(program, {
67
+ cwd,
68
+ prOpenDependencies: options.prOpenDependencies,
69
+ stderr,
70
+ stdout,
71
+ });
63
72
  await program.parseAsync(["node", "gji", ...argv], { from: "node" });
64
73
  return { exitCode: 0 };
65
74
  }
@@ -118,19 +127,33 @@ function registerCommands(program) {
118
127
  .action(notImplemented("new"));
119
128
  program
120
129
  .command("init [shell]")
121
- .description("print or install shell integration")
130
+ .description("set up shell integration interactively or print a shell wrapper")
122
131
  .option("--write", "write the integration to the shell config file")
132
+ .option("--json", "emit a JSON error in non-interactive mode")
123
133
  .action(notImplemented("init"));
134
+ program
135
+ .command("doctor")
136
+ .description("check gji installation and configuration health")
137
+ .option("--fix", "apply safe automatic fixes after showing the plan")
138
+ .option("--yes", "apply --fix without prompting")
139
+ .option("--json", "emit diagnostic checks as JSON")
140
+ .action(notImplemented("doctor"));
124
141
  program
125
142
  .command("completion [shell]")
126
143
  .description("print shell completion definitions")
127
144
  .action(notImplemented("completion"));
128
- program
145
+ const prCommand = program
129
146
  .command("pr <ref>")
147
+ .usage("[options] <ref>")
130
148
  .description("fetch a pull request by number, #number, or URL into a linked worktree")
131
149
  .option("--dry-run", "show what would be created without executing any git commands or writing files")
132
150
  .option("--json", "emit JSON on success or error instead of human-readable output")
133
151
  .action(notImplemented("pr"));
152
+ prCommand
153
+ .command("open [target]")
154
+ .description("open an existing pull request in the default browser")
155
+ .option("--select", "choose a pull request from any linked worktree")
156
+ .action(notImplemented("pr open"));
134
157
  program
135
158
  .command("back [n]")
136
159
  .description("navigate to the previously visited worktree, optionally N steps back")
@@ -267,6 +290,7 @@ function attachCommandActions(program, options) {
267
290
  ?.action(async (shell, commandOptions) => {
268
291
  const exitCode = await runInitCommand({
269
292
  cwd: options.cwd,
293
+ json: commandOptions.json,
270
294
  shell,
271
295
  stderr: options.stderr,
272
296
  stdout: options.stdout,
@@ -276,6 +300,21 @@ function attachCommandActions(program, options) {
276
300
  throw commanderExit(exitCode);
277
301
  }
278
302
  });
303
+ program.commands
304
+ .find((command) => command.name() === "doctor")
305
+ ?.action(async (commandOptions) => {
306
+ const exitCode = await runDoctorCommand({
307
+ cwd: options.cwd,
308
+ fix: commandOptions.fix,
309
+ json: commandOptions.json,
310
+ stderr: options.stderr,
311
+ stdout: options.stdout,
312
+ yes: commandOptions.yes,
313
+ });
314
+ if (exitCode !== 0) {
315
+ throw commanderExit(exitCode);
316
+ }
317
+ });
279
318
  program.commands
280
319
  .find((command) => command.name() === "completion")
281
320
  ?.action(async (shell) => {
@@ -303,6 +342,22 @@ function attachCommandActions(program, options) {
303
342
  throw commanderExit(exitCode);
304
343
  }
305
344
  });
345
+ const prOpenCommand = program.commands
346
+ .find((command) => command.name() === "pr")
347
+ ?.commands.find((command) => command.name() === "open");
348
+ const runPrOpenCommand = createPrOpenCommand(options.prOpenDependencies);
349
+ prOpenCommand?.action(async (target, commandOptions) => {
350
+ const exitCode = await runPrOpenCommand({
351
+ cwd: options.cwd,
352
+ stderr: options.stderr,
353
+ stdout: options.stdout,
354
+ select: commandOptions.select,
355
+ target,
356
+ });
357
+ if (exitCode !== 0) {
358
+ throw commanderExit(exitCode);
359
+ }
360
+ });
306
361
  program.commands
307
362
  .find((command) => command.name() === "back")
308
363
  ?.action(async (n, commandOptions) => {
@@ -0,0 +1,28 @@
1
+ export type DoctorCheckStatus = "fail" | "ok" | "skip";
2
+ export interface DoctorCheck {
3
+ hint?: string;
4
+ id: string;
5
+ message: string;
6
+ status: DoctorCheckStatus;
7
+ }
8
+ export type DoctorFixStatus = "applied" | "declined" | "failed" | "pending" | "skipped";
9
+ export interface DoctorFix {
10
+ hint?: string;
11
+ id: string;
12
+ message: string;
13
+ paths?: string[];
14
+ status: DoctorFixStatus;
15
+ }
16
+ export interface DoctorCommandOptions {
17
+ cwd: string;
18
+ home?: string;
19
+ fix?: boolean;
20
+ json?: boolean;
21
+ interactive?: boolean;
22
+ confirmFixes?: (fixes: DoctorFix[]) => Promise<boolean>;
23
+ shell?: string;
24
+ yes?: boolean;
25
+ stderr?: (chunk: string) => void;
26
+ stdout: (chunk: string) => void;
27
+ }
28
+ export declare function runDoctorCommand(options: DoctorCommandOptions): Promise<number>;