Skip to content

Add JSON output to gh auth status - #11544

Merged
BagToad merged 31 commits into
cli:trunkfrom
benjlevesque:auth-status-json-output-8635
Sep 25, 2025
Merged

BagToad merged 31 commits into
cli:trunkfrom
benjlevesque:auth-status-json-output-8635

Conversation

@benjlevesque

@benjlevesque benjlevesque commented Aug 20, 2025 •

Copy link
Copy Markdown
Contributor

This PR adds a --json flag to the gh auth status command.
--jq and --template are also available

Resolves #8637
Acceptance criteria: #8637 (comment)

@benjlevesque benjlevesque changed the title Fix missing assertions Add JSON output to gh auth status Aug 20, 2025
@benjlevesque
benjlevesque marked this pull request as ready for review August 20, 2025 21:09
@benjlevesque
benjlevesque requested a review from a team as a code owner August 20, 2025 21:09
@cliAutomation cliAutomation added the external pull request originating outside of the CLI core team label Aug 20, 2025
@evilhamsterman

Copy link
Copy Markdown

This look great to me!

@BagToad
BagToad self-requested a review August 25, 2025 14:58
@BagToad
BagToad requested a review from babakks August 25, 2025 16:26

@babakks babakks left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR, @benjlevesque! 🙏

I haven't yet fully reviewed it, just had a comment on the -t conflict case to trigger a discussion.

Comment thread pkg/cmdutil/json_flags.go Resolved
@babakks
babakks self-requested a review September 5, 2025 08:20
@babakks

babakks commented Sep 25, 2025

Copy link
Copy Markdown
Member

This PR works great for a project of mine, where I just want a programmatic way to tell if gh CLI has signed in:

@srid, I'm not sure what exactly you mean by signed in (e.g. as in having a valid/working token, or just have signed in at some point), but please note that:

  • auth status is not a static command; it talks to the API to make sure the tokens are still valid.
  • seeing active: true does not mean the token is valid/working. Rather it just means gh will use that user's token by default, unless you run gh auth switch to make another user active. I can see the terminology is a bit confusing here.
  • the state field should be the only source of truth if you need to find a valid/working token (i.e. state: success).

@babakks
babakks requested a review from BagToad September 25, 2025 10:02
@srid

srid commented Sep 25, 2025 •

Copy link
Copy Markdown

Ah, I just noticed that hosts."github.com" is a list.

  • Is there exactly one active token at a time?

If so, how can the JSON be restructured to reliably get that active token? In the current schema, it is possible for arbitrary number of tokens to be active, since it is just a list.

@srid

srid commented Sep 25, 2025

Copy link
Copy Markdown

I'm not sure what exactly you mean by signed in (e.g. as in having a valid/working token

Basically, I want to make sure that the user has gh auth login'ed once, so that running https://github.com/basecamp/gh-signoff will succeed.

@babakks

babakks commented Sep 25, 2025 •

Copy link
Copy Markdown
Member
  • Is there exactly one active token at a time?

Yes, only one entry per hostname can be active. You can run the command with --active flag to limit the results to just the active entries. However, if there are more than one host, you'd get one active entry per each. You can also use --hostname <HOST> to filter the only one you're interested in.

UPDATE: if you already know the hostname, technically you can try gh auth token --hostname <HOST> which will return the token for the active user for the given host. The upside is that there'll be no API roundtrip, but the downside is that you're going to get the user's token, which depending on your use case could be okay or not. I'd say you better off using auth status to be on the safe side and not doing anything that might be perceived as malicious to your users.

@BagToad
BagToad merged commit b76bc77 into cli:trunk Sep 25, 2025
@chedoe

chedoe commented Sep 29, 2025

Copy link
Copy Markdown

#11822

chedoe

This comment was marked as spam.

tmeijn pushed a commit to tmeijn/dotfiles that referenced this pull request Oct 10, 2025
This MR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [cli/cli](https://github.com/cli/cli) | minor | `v2.80.0` -> `v2.81.0` |

MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot).

**Proposed changes to behavior should be submitted there as MRs.**

---

### Release Notes

<details>
<summary>cli/cli (cli/cli)</summary>

### [`v2.81.0`](https://github.com/cli/cli/releases/tag/v2.81.0): GitHub CLI 2.81.0

[Compare Source](cli/cli@v2.80.0...v2.81.0)

#### Support for GitHub Release Attestations

This release introduces the `release verify` and `release verify-asset` commands for verifying GitHub Release attestations. Part of the Immutable Releases initiative, a release attestation provides a signed, timestamped binding between a release, its git tag, and any associated assets. These new commands provide a convenient way to verify the integrity of an immutable release against its attestation.

- Verify the latest release has a valid attestation: `gh release verify`
- Verify a specific release by tag: `gh release verify v1.2.3`
- Verify an asset from the latest release: `gh release verify-asset my-asset.zip`
- Verify a local asset file originated from a specific release: `gh release verify-asset v1.2.3 my-asset.zip`

These commands help ensure that releases and their assets are authentic and haven’t been tampered with, providing an additional layer of security for your software supply chain.

#### `gh auth status` Supports JSON Output

This release adds support for the `--json` flag in `gh auth status`. Run `gh auth status --help` for more information and usage examples.

#### What's Changed

##### ✨ Features

- Add alias `co` for `pr checkout` by [@&#8203;babakks](https://github.com/babakks) in [#&#8203;11804](cli/cli#11804)
- Add JSON output to `gh auth status` by [@&#8203;benjlevesque](https://github.com/benjlevesque) in [#&#8203;11544](cli/cli#11544)
- `release verify`  and `release verify-asset` are now visible by [@&#8203;ejahnGithub](https://github.com/ejahnGithub) in [#&#8203;11801](cli/cli#11801)

##### 🐛 Fixes

- Fix no tagname logic for  release verify-asset  by [@&#8203;ejahnGithub](https://github.com/ejahnGithub) in [#&#8203;11798](cli/cli#11798)

##### 📚 Docs & Chores

- refactor: use strings.FieldsFuncSeq to reduce memory allocations by [@&#8203;juejinyuxitu](https://github.com/juejinyuxitu) in [#&#8203;11805](cli/cli#11805)

#### New Contributors

- [@&#8203;juejinyuxitu](https://github.com/juejinyuxitu) made their first contribution in [#&#8203;11805](cli/cli#11805)

**Full Changelog**: <cli/cli@v2.80.0...v2.81.0>

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this MR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xMzIuNSIsInVwZGF0ZWRJblZlciI6IjQxLjEzMi41IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJSZW5vdmF0ZSBCb3QiXX0=-->
tianjianjiang added a commit to tianjianjiang/smith that referenced this pull request Aug 26, 2026
The GitHub CLI Live State probe chained command -v gh, gh auth
status, and echo fallbacks with &&/||/{}, which a Claude Code
worktree-isolation guard refuses to run ("too complex to verify
that it stays inside the worktree"), breaking /smith-preflight
(and by extension /smith-ship step 0) for every worktree-isolated
session.

Split into two unchained probes: command -v gh, and gh auth
status --json hosts --active --jq '...state...' — using gh's own
documented machine-readable field instead of prose-matching,
scoped to the active account, requires gh >= 2.81.0 (cli/cli#11544).

Assisted-by: Claude:claude-sonnet-5
yshrsmz added a commit to yshrsmz/mewgit that referenced this pull request Sep 9, 2026
* feat(auth): GhCli で gh から認証状態とトークンを取る

detect / auth_status / token の 3 本。実物の `gh` 2.100.0 と cli/cli のソースで
形を確かめてから書いた。

**テキスト出力は解析しないと決めた(設計変更)。** 04 は「JSON とテキスト両対応」と
書いていたが、`gh auth status --json hosts` が入ったのは 2.81.0(2025-10-01
リリース。cli/cli#11544 のマージが 2025-09-25。`gh api` でリリースを引いて確認)で、
`--user` も同じ版に既にある(v2.81.0 の `token.go` を読んで確認)。それ未満は
`AuthError::GhTooOld` で degraded に落とす。

理由は**書式を確かめられないまま書くことになる**から。手元の 2.100.0 のテキストは
`✓ Logged in to github.com account <login> (<source>)` / `- Active account: true` /
スコープはクォート付きだが、古い版は `as <login>` でクォート無し、
`- Active account:` の行自体が無い。再現できない書式にパーサを書くのは
`.claude/rules/verifying-assumptions.md` に反する。`gh` はもともと必須依存なので、
要求版を案内するほうが行動可能。古い gh の利用者が出てきたら、その人の実出力を
フィクスチャにして足すほうが確実。

実物で分かって設計に反映したこと。

- **同じ `login` が複数のエントリで出る。** 実機で `GITHUB_TOKEN`(環境変数)と
  `keyring` が、どちらも同じ login で並んだ。`GhLogin` は `tokenSource` を持たない
  ので**見分けられない重複**になり、`resolve` の「1 件なら `Single`」が誤爆する。
  `(host, login)` で畳んで `active` は OR を取る
- **`scopes` はカンマ区切りの 1 本の文字列**で `omitempty`。fine-grained PAT では
  丸ごと欠ける(04 の EMU の節が「`scopes` が空でも動作させる」と言っている形)
- **`state` は `success` / `timeout` / `error` の 3 値**(v2.100.0 の `status.go`)。
  採るのは `success` だけ。`timeout` を `error` と同じに畳んでよいかは
  `auth::resolve` へ送った
- **`--show-token` は付けない。** 付けると JSON に生のトークンが入る(不変ルール 3)
- `bin: PathBuf` は持たない。差し替える要求が無く、#21 で `Runner.git_bin` を
  同じ理由で消してある

テストは 13 本。版の比較と JSON の畳み込みは純粋関数として、**残り 3 本は実物の
`gh` を叩く**(`ssh` のテストと同じ方針)。`gh` は設定ディレクトリを環境変数でしか
受けないので、`Runner::with_env` と同じ形の `GhCli::with_env` を足し、
`GH_CONFIG_DIR` とトークンの環境変数で実行環境から切り離した。

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_012MHJpyKE69BFZ2wdU3mfkN

* refactor(auth): /simplify の指摘を反映する

4 観点で 20 件。実質的なものが 4 つあった。

**`config` の `general.gh_bin` が黙って死んでいた。** `"gh"` を直書きしていたので、
設定・既定値・`MEWGIT_GH_BIN` の上書きとそのテスト 3 本が実装に到達していなかった。
**しかも根拠に引いた #21 が逆だった** —— 実際のコミット本文は「`Runner` に `git_bin` を
**戻し**、`config` の `general.git_bin` から渡す形にした。設計書 01 と 08 は最初から
『ランタイムが読む』と書いており…」で、#21 はまさに今回と同じ穴を塞いだ PR。
`GhCli::detect(bin)` にして、#21 と同型の見張り(直書きに戻すと落ちるテスト)を足した。
04 と doc コメントの誤った引用も直し、08 の「任意コード実行になる」を 2 つの bin で数えた。

**`state` を gh の層で絞ると、04 が `resolve` へ送った判断の材料が届かない。**
04 は「`Timeout` を `Error` と同じに畳んでよいかは `auth::resolve` で決める」と
書いているのに、`success` だけ採って `GhLogin` に `state` を持たせていなかった。
`LoginState` を足して落とさずに運ぶ。畳むときは `state` が一番良かったものを採り、
**スコープは `Success` なエントリのものを採る** —— 出どころが違えば別のトークンなので
スコープも違いうる(「同じはず」は確かめていない断定だった)。

**`auth_status` が全ホストを検証していた。** `--hostname` を付けないと gh は
config にある全ホストのトークンを検証するので、到達できないホストが 1 つあるだけで
**実測 30.2 秒**(絞れば 0.24 秒)。gh に待ち時間を縮めるフラグは無い。04 の手順 2 が
言う「`target.host` で絞る」を、結果ではなく argv でやる形にした。config に無い
ホストでも exit 0 で `{"hosts":{}}` が返るので「0 件なら `NotLoggedIn`」は変わらない。

**「証」を名乗る型が検査を通さずに作れた。** `Default` + `pub with_env` があったので
`GhCli::default().auth_status()` が通り、テスト自身がその抜け道を使っていた(04 には
「持つ値は無い」と書いてしまっていた)。構築経路を `detect` 1 本に絞り、版判定を
純粋関数 `check_version` に切り出して **`GhTooOld` の腕を初めてテストで踏めるように**した。

`ssh.rs` が「`GhCli` が 3 本目になったら数え直す」と送っていた宿題も片付けた。数えると
3 本ではなく「同形が 2 本 + 別物が 1 本」で、`git_exec::Runner::spawn` は前提が違う
(`process_group`、`kill_on_drop` を敢えて付けない、stdin パイプ、キャンセル)。
`ssh -G` と `gh` を `auth::probe` に寄せた結果、**共有していなかったぶんの実費**も
消えた —— `LC_ALL=C` が無い、起動失敗の `io::Error` を捨てて全部 `GhMissing`
(権限エラーまで「見つかりませんでした」になっていた)、exit code を落としている。

そのほか。

- `AuthError::GhTooOld` から `required` を落とす(常に `REQUIRED` からの導出値)。
  `REQUIRED` を `pub` にして `ui` からも読めるようにした
- `merge_logins` を `BTreeMap` にして、線形探索・末尾の `sort_by`・「順は不定」の
  コメントを 1 つに畳んだ。捨てるための `scopes` パースも走らなくなった
- カンマ区切りスコープの分割を `auth::split_scopes` に寄せた(`github::client` の
  `needed_scopes` が 2 人目)
- stdout のコピーを 1 回に(`from_utf8` で `Vec<u8>` を転用)。`token` も
  `trim_end` で切り詰めて `String` ごと渡し、平のトークンを載せた中間バッファを増やさない
- `parse_version` から「パッチ無しも受ける」を落とした。gh がその形を出す実例を見ていない
- `requirements.md` の degraded 事由に「版が古い」を足した(4 つ目)

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_012MHJpyKE69BFZ2wdU3mfkN

* fix(auth): /code-review の指摘を直す

5 件。

**login が空のエントリを候補に数えていた。** 期限切れの `GH_TOKEN` を export した
ままだと、本物のログインの隣に `{"state":"error","active":true,"login":""}` が並ぶ
(レビューが実測)。`token(host, user)` は login で引くので**取り出す術が無い**のに
候補として数えるので、04 の手順 3「1 件なら `Single`」が誤爆する。落とすようにした。
**`state` の絞り込みとは違う** —— こちらは `resolve` の方針で変わる余地が無い
(どの方針でも使えない)ので、gh の層で落としてよい。

**`scopes.is_empty()` を「まだ埋めていない」の印にしていた。** fine-grained PAT は
本当にスコープが空なので(04 の EMU の節)、1 件目がそれで 2 件目が別のトークンだと、
**持っていないスコープを報告する**。「足りないスコープ」の案内が嘘になる。
`scopes_taken` を別に持って、印と値を割らない形にした。

**`GhMissing` の案内にパスが入っていなかった。** `general.gh_bin` や `MEWGIT_GH_BIN`
の書き間違いでもここに来るので、「gh が見つかりませんでした」だけだと PATH を
探させてしまう。

**04 に古い段落が残っていた。** 「`bin: PathBuf` は持たない … `Command::new("gh")` の
PATH 解決に任せる」が、48 行上の構造体と、同じコミットで足した箇所の両方と矛盾した
まま生き残っていた。しかも**直前のコミットが「誤りだった」と書いた #21 の逆読み**が
そこに残っていたので、次に読む人が `bin` を消して同じ穴を開け直せる状態だった。

**版のぶら下がりで全機能を落としていた。** `2.85.0+dfsg1` や `2.90.0-rc.1` が
`GhTooOld` に落ちる。**`/simplify` の「実例を見ていない一般性は入れない」と逆を
向く指摘だが、実害がある側を採った** —— 厳しくして外すと十分新しい gh で GitHub 機能を
丸ごと失い、緩めて外しても比較に使う 3 つの数は変わらない。3 つ揃っていることは
引き続き求め、**パッチが数字だけで終わっているのに 4 つ目が続く形**(`2.81.0.1`)は
知らない採番なので受けない。

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_012MHJpyKE69BFZ2wdU3mfkN

---------

Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
immakermatty added a commit to Lazurio/LazurioPlatform that referenced this pull request Sep 28, 2026
…-in refused in CLI and routes, the broker's identity shown, sign-out of a leftover personal account (#66)

* Team Environment: gh is not signed in with a person's account; a person's account left there can be signed out

Principal 2026-09-28: a Team Environment (hosted-organization-team) works in
GitHub through Lazurio for GitHub, set up by the Organization. One pure rule
(src/tools/team-github.ts, githubActionRefused) refuses the curated gh
sign-in and SSH key linking there and allows gh's sign-out only while the
active account is a person's stored sign-in (never a [bot] App identity, an
installation token or a token from a variable). The CLI reads the preset of
the hosted operator Folder (hostedOperatorFolder, as lazurio update does);
the Launchpad reads its Folder's preset per request. Refusals are the usual
blocked result: reason team-environment, exit 2 / HTTP 409. The gh probe
reports identity; the Team gh row offers Sign out only for a person's
account and its status line says "uses Lazurio for GitHub". Docs: F19
addendum, environment tools, workspace presets, Launchpad development.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* Team Environment: brokered gh reads as the Organization's bot; gh's prompt and install sign nobody in

gh's sign-in probe asks `gh auth status --json hosts` first (gh 2.81.0,
cli/cli#11544), the one gh auth command the Organization's brokered gh
answers, and falls back to the text form for an older gh. One field, never
--show-token, so the token is not in the answer. The same reading serves the
status, login confirmation, the scope check and the key removal
(src/tools/gh-status.ts). On a Team Environment the App identity reads
"Works as lazurio-for-github[bot]" next to "Uses Lazurio for GitHub", with
no sign-out and a refused logout.

`lazurio tools prompt gh` reads the hosted operator Folder's preset and
prints gh's Team target state there instead of the sign-in; elsewhere the
prompt adds the Team sentence. `tools install gh` on a Team Environment ends
with the Team sentence instead of "Next: lazurio tools login gh"; the
Launchpad's Team gh row offers "Install" only while gh is missing. The fake
gh gains the broker mode and a gh without --json. Docs: F19 addendum,
environment tools, Launchpad development; known limits kept.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* Team Environment: a running gh session re-checks the rule before every step that changes the account or the Machine

Review of PR 66: a gh login started on a Work Folder finished, SSH key
linking included, after the preset changed to hosted-organization-team. The
login core now asks the rule again (LoginEnvironment.refused) after the
device flow, around a scope refresh, before the key linking starts, before
the key's creation, its registration, the known_hosts write and the final
"signed in", and ends the session as blocked / team-environment. The
Launchpad ends a running gh session when /api/update makes its Folder a
Team Environment, and a gh poll there ends the session with the refusal.
An unreadable preset fails the Launchpad session closed
(environment-unreadable). Regression tests: each checkpoint in turn, and a
Work-to-Team switch through the real profile update mid-login.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* Folder reads wait for the operation lock instead of failing as busy

The flaky gh login test of PR 66 (launchpad-curated with update-path-entry,
and macOS CI) was not shared test state: under load, the Launchpad's poll
gate and the running gh session's Team-rule re-check read the Folder's
preset at the same moment, and the Folder operation lock is exclusive and
non-blocking (flock LOCK_NB), so one read was refused as busy: the poll
answered operation-failed (or the session failed environment-unreadable).
Reads (the Team rule's preset, the tools status, readFolderTools,
/api/profile) now go through withFolderReadLock, which retries only on the
new FolderOperationBusyError for up to 3 s; mutations keep the immediate
refusal and the lock messages are unchanged. Regression tests: 24 parallel
preset reads, a read waiting for a holder, the bound, and a Launchpad gh
login completing while another reader keeps taking the lock.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

---------

Co-authored-by: Claude Fable 5.1 <[email protected]>
eharriett0 added a commit to eharriett0/wt that referenced this pull request Oct 10, 2026
…e a host by its active account (#203) (#216)

Root causes:
- gh's human `auth status` output reports every non-timeout error (DNS
  failure, connection refused) as "X Failed to log in ... token invalid", so
  offline, doctor said "NOT authenticated".
- In a repo the check is scoped (`--hostname H`, #100/#102) and was read from
  the exit code alone: a timeout on H said "NOT authenticated", and an
  INACTIVE account with a dead token on H exits 1 as well, so Authed() gated
  every gh call off (PR lookups, liveness) while the active account worked.

Fix:
- ghx asks `gh auth status --json hosts --active [--hostname H]` first (gh
  2.81+, cli/cli#11544; shape read from gh's source at v2.81.0 and v2.102.0).
  That form exits 0 whatever the login, keeps gh's error per account, and
  --active checks only the account gh uses. It is read from stdout alone
  (pure parseAuthStatusJSON): the active entry decides a host, none marked is
  unknown. Pure classifyAuthError: only a 401 is a dead token (REST "HTTP 401:
  ...", GraphQL for GH_TOKEN "non-200 OK status code: 401 ..."); no HTTP answer
  (`Get "<url>": ...`) is the new state "unreachable"; any other status (a 403,
  a 5xx) is unknown.
- When gh refuses the flag ("unknown flag: --json", before 2.81) the SAME
  memoized check falls back to the #183 human-form parser, and the refusal is
  remembered per process (authNoJSON): gh < 2.81 spends one local flag error
  per process, gh 2.81+ still asks once per command (#172). Any other failure
  falls back once, not remembered (pure jsonAuthOutcome).
- AuthedFor/Authed decide by each host's ACTIVE account (pure authOK), not the
  exit code. A human-form exit 1 is a yes only when an inactive account's
  failure in a section wt read explains it (textAuthStatus): Authed() sets
  PRChecked, which dormancy relies on, so a failure wt cannot see stays a no.
  Unreadable output stays a no.
- doctor reads a scoped check per host too: a timeout or unreachable repo host
  says "could not be verified" and names it, never "NOT authenticated"; a
  scoped check wt can't read names its host. A human-form "failed" (gh < 2.81)
  can still be an unreachable host, so that NOT-authenticated line says "or
  offline". JSON, additive: gh_hosts[].state gains "unreachable", gh_hosts[]
  gains "error" (gh's text, 2.81+), and a scoped check that did not pass now
  carries gh_hosts/gh_auth_unknown; a check that passed is unchanged.

Measured with a fake gh first on PATH, one shim per generation (2.102-style
JSON; 2.68.1's verbatim refusal of --json), HOME a temp dir, before vs after:
  offline DNS failure, outside a repo:
    before, both: gh - found but NOT authenticated (...)
    after, 2.81+: gh - found, but auth could not be verified: couldn't reach
                  ghe.example.com, couldn't reach github.com
    after, 2.68:  gh - found but NOT authenticated, or offline: gh before 2.81
                  reports a host it cannot reach as a failed login (...)
  in a github.com-origin repo:
    offline:   NOT authenticated -> (2.81+) could not be verified: couldn't
               reach github.com
    timeout:   NOT authenticated -> (both) could not be verified: github.com
               timed out
    inactive dead account: NOT authenticated and `wt status` ran no PR lookup
               -> (both) gh - authenticated, and `wt status` runs `gh pr list`
               for both windows
    dead token (401): NOT authenticated before and after (2.68: "or offline")
  a passing check and the #183 two-host case: the gh line is unchanged.
  gh runs per doctor: 1 (2.81+), 2 (2.68: the flag error, then the human form).
Mutation checks in a scratch copy: 25 mutants (exit code decides, the JSON
form's exit code trusted, every gh error a dead token, active marker ignored,
no fallback, refusal not remembered, one stream for the JSON run, no ANSI
strip, an unexplained exit 1 read as a yes, doctor's pre-#203 scoped reading,
the caveat on a 401, ...) are all killed.

Fixes #203

Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

external pull request originating outside of the CLI core team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gh auth status should have a JSON output option

8 participants