Skip to content

feat(cli): treat --json as unattended mode#1420

Merged
gu-stav merged 2 commits into
mainfrom
feat/json-implies-unattended
Jul 3, 2026
Merged

feat(cli): treat --json as unattended mode#1420
gu-stav merged 2 commits into
mainfrom
feat/json-implies-unattended

Conversation

@gu-stav

@gu-stav gu-stav commented Jul 2, 2026

Copy link
Copy Markdown
Member

Description

A command that emits machine-readable output can't stop at a prompt — the caller parsing its stdout has no way to answer. --json should therefore behave like --yes: use defaults, error on missing input, never prompt.

Rather than each command re-deriving this, --json is folded into SanityCommand.isUnattended(). Every command that already respects unattended mode now also skips prompts under --json.

What to review

  • SanityCommand.isUnattended() now returns true for --json as well as --yes/non-TTY.
  • The commands that branch on it and expose a --json flag inherit the change: init, projects create, tokens add (and deploy, once its refactor lands). Notably tokens add --json now uses the default viewer role instead of prompting.

Testing

tokens add's --json unit test was updated to reflect that the role now defaults instead of prompting (no roles fetch). Other affected commands' unit tests pass unchanged.


Note

Medium Risk
Central CLI behavior change affects every command using isUnattended() with --json, which may alter defaults (e.g. token role) for scripts that previously hung on prompts.

Overview
SanityCommand.isUnattended() now treats --json like --yes and non-TTY stdin: commands must not prompt and should use defaults or fail with a non-zero exit when input is missing. The docblock explains that machine-readable callers cannot answer interactive prompts.

Any command that already branches on isUnattended() and exposes --json picks up this behavior centrally (e.g. tokens add --json defaults the role to viewer instead of fetching roles and prompting).

The tokens add --json test was aligned with that: no roles API mock, and assertions that select / input are never called. A changeset records a minor @sanity/cli-core release.

Reviewed by Cursor Bugbot for commit 41138e8. Bugbot is set up for automated code reviews on this repo. Configure here.

@gu-stav gu-stav requested a review from a team as a code owner July 2, 2026 08:51
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle Stats — @sanity/cli

Compared against main (38480893)

@sanity/cli

Metric Value vs main (3848089)
Internal (raw) 2.7 KB -
Internal (gzip) 1.0 KB -
Bundled (raw) 11.16 MB -
Bundled (gzip) 2.10 MB -
Import time 918ms +20ms, +2.2%

bin:sanity

Metric Value vs main (3848089)
Internal (raw) 782 B -
Internal (gzip) 423 B -
Bundled (raw) 9.87 MB -
Bundled (gzip) 1.78 MB -
Import time 2.33s +3ms, +0.1%

🗺️ View treemap · Artifacts

Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

📦 Bundle Stats — @sanity/cli-core

Compared against main (38480893)

Metric Value vs main (3848089)
Internal (raw) 105.7 KB +121 B, +0.1%
Internal (gzip) 25.8 KB +67 B, +0.3%
Bundled (raw) 21.72 MB +121 B, +0.0%
Bundled (gzip) 3.46 MB +56 B, +0.0%
Import time 805ms -5ms, -0.7%

🗺️ View treemap · Artifacts

Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

📦 Bundle Stats — create-sanity

Compared against main (38480893)

Metric Value vs main (3848089)
Internal (raw) 908 B -
Internal (gzip) 483 B -
Bundled (raw) 931 B -
Bundled (gzip) 491 B -
Import time ❌ ChildProcess denied: node -
Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 013f630. Configure here.

Comment thread packages/@sanity/cli-core/src/SanityCommand.ts
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Coverage Delta

File Statements
packages/@sanity/cli-core/src/SanityCommand.ts 80.0% (±0%)

Comparing 1 changed file against main @ 3848089387f2948ea948cb6c4d992e6aedbab09b

Overall Coverage

Metric Coverage
Statements 73.6% (±0%)
Branches 63.8% (±0%)
Functions 67.5% (+ 0.0%)
Lines 74.2% (±0%)

@gu-stav gu-stav force-pushed the feat/json-implies-unattended branch from 013f630 to 6ee82bc Compare July 2, 2026 09:01
@gu-stav gu-stav changed the base branch from feat/deploy-json to main July 2, 2026 09:02
@gu-stav gu-stav requested a review from runeb July 2, 2026 09:06
gu-stav added a commit that referenced this pull request Jul 2, 2026
MCP setup switched to non-prompting `auto` only for --yes, so any other
unattended run — a non-TTY, or --json once it implies unattended (#1420) —
still stopped at the MCP prompt. Gate it on isUnattended() so a
non-interactive init never blocks there.
@gu-stav gu-stav force-pushed the feat/json-implies-unattended branch from 6ee82bc to a04fa3d Compare July 2, 2026 09:17
@gu-stav gu-stav requested a review from snocorp July 3, 2026 14:13
A command that emits machine-readable output can't stop at a prompt — the
caller parsing its stdout has no way to answer. Fold --json into
isUnattended() so every command that already respects unattended mode also
skips prompts under --json.
@gu-stav gu-stav force-pushed the feat/json-implies-unattended branch from a04fa3d to d0ba803 Compare July 3, 2026 14:14
snocorp
snocorp previously approved these changes Jul 3, 2026

@snocorp snocorp left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good, just left one question about the test

uri: '/projects/test-project/roles',
}).reply(200, mockRoles)

// --json is unattended, so the role defaults to viewer without a roles prompt

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we assert that select/input is not called as well?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good idea 👍🏼 Added in 41138e8

@snocorp

snocorp commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

It might also be good to audit the use of flags.yes to see if we should be using isUnattended instead.

@gu-stav

gu-stav commented Jul 3, 2026

Copy link
Copy Markdown
Member Author

It might also be good to audit the use of flags.yes to see if we should be using isUnattended instead.

I did that and apart from #1421 everything seems good.

@gu-stav gu-stav merged commit 05f768b into main Jul 3, 2026
56 checks passed
@gu-stav gu-stav deleted the feat/json-implies-unattended branch July 3, 2026 14:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants