Repository navigation
Conversation
There was a problem hiding this comment.
Copilot review overview
🟢 Approval recommended
The documentation is consistent with existing CLI contracts and its new internal links resolve correctly.
Review effort: Balanced
Findings: None
What changed in this PR
Adds public contributor guidance for designing consistent Shopify CLI commands and terminal experiences.
Changes:
- Adds CLI outcome, lifetime, automation, and progress guidance.
- Expands command syntax, flag, prompt, banner, and logging conventions.
- Links the new guidance from the contributor documentation index.
| File | Description |
|---|---|
docs/README.md |
Links the new design guide. |
docs/cli/designing-for-cli.md |
Introduces CLI design principles. |
docs/cli-kit/ui-kit/guidelines.md |
Expands content and terminal UI guidance. |
docs/cli-kit/command-guidelines.md |
Clarifies command structure, flags, options, and help text. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
|
||
| Use UI Kit's semantic tokens for commands, user input, and other styled text. Use its standard banner types rather than assigning your own colors. The [token system](./readme.md#the-token-system) keeps these meanings consistent without requiring commands to manage a palette. | ||
|
|
||
| Developers can customize their terminal colors. A terminal mockup can start with a 16-color palette, but do not depend on exact hues. Keep the standard associations of red with errors, yellow with warnings, and green with success. Always communicate the meaning through text as well as color. |
There was a problem hiding this comment.
I don't think this is worded strongly enough. The point is that the CLI output should be completely understandable in no color mode, both for accessibility reasons and for environments like CI systems where color or other text decorations like bold/italic/underline may not be supported.
There was a problem hiding this comment.
Updated in 510d8a7.
Strengthened: "Output must be completely understandable without color or text decoration." It names accessibility, --no-color, and CI environments without color, bold, italic, or underline. I removed the mockup-palette sentence.
|
|
||
| Developers can customize their terminal colors. A terminal mockup can start with a 16-color palette, but do not depend on exact hues. Keep the standard associations of red with errors, yellow with warnings, and green with success. Always communicate the meaning through text as well as color. | ||
|
|
||
| Use emojis sparingly to draw attention or clarify meaning, not as decoration. Keep neutral output quiet. An active indicator, such as UI Kit's animated progress bar, should distinguish running work from completed or failed work. |
There was a problem hiding this comment.
I believe our policy is not to use emojis at all, because not all terminals and environment support them. We do support a limited set of symbols through the figures package.
There was a problem hiding this comment.
Updated in 510d8a7.
Changed to "Do not use emojis," and to use the symbols from @shopify/cli-kit/node/figures. I also replaced the ❌ in the log example with ✖ (figures.cross).
One question: the app dev session logger currently prints ❌ Error, ✅ Ready… and ⚠️ (dev-session-logger.ts, dev-session.ts), and app logs polling uses ❌. Should those move to figures symbols, or is dev-session output an intended exception?
|
|
||
| ## Use color and emphasis with purpose | ||
|
|
||
| Use grayscale text for neutral information. Use brighter foreground text for emphasis. Reserve color for semantic meaning, such as success, warnings, and errors, or to connect related log entries, such as entries from the same extension. |
There was a problem hiding this comment.
There was a problem hiding this comment.
Updated in 510d8a7.
Thanks, that matches what I see in the code. The text now says to use the default text style for most output, and the subdued token as a gray accent that does not suggest highlighting, such as a table's row-title column. I made the same change in the Logs section, which repeated the grayscale rule.
| | --- | --- | | ||
| | Text entry | A value such as an app name or version name. Offer an editable default when it helps the developer continue. | | ||
| | Single select | A list of choices. Group related choices when that makes the list easier to scan. | | ||
| | Confirmation | A high-risk choice. Use sparingly, such as before deploying an app version that removes an extension. | |
There was a problem hiding this comment.
Technically Dangerous Confirmation - also, I would define it as being for one-way doors, such as irretrievably deleting information (which is why removing an extension is dangerous - because when the app is deployed, there is the potential for information to be lost).
There was a problem hiding this comment.
Updated in 510d8a7.
Split into two rows. "Confirmation" is an ordinary yes-or-no choice. "Dangerous confirmation" is for one-way doors, such as irretrievably deleting information. Its example says that deploying a version that removes an extension can permanently delete that extension's data, and that the developer types a confirmation value.
| | Single select | A list of choices. Group related choices when that makes the list easier to scan. | | ||
| | Confirmation | A high-risk choice. Use sparingly, such as before deploying an app version that removes an extension. | | ||
|
|
||
| An editable default lets the developer press Enter to accept a suggested name or type their own. Show the choice rather than making an unexplained inference. See the [prompt APIs](./readme.md#prompts) for supported defaults and grouping. |
There was a problem hiding this comment.
This isn't true for text prompts - the default is blank, and the user presses Tab to accept the default.
There was a problem hiding this comment.
Updated in 510d8a7.
Right, the input starts blank. I checked the code to describe both keys. TextInput inserts the placeholder on Tab (TextInput.tsx L79–82), and TextPrompt also submits the default on Enter when the answer is empty (answerOrDefault, TextPrompt.tsx L62). The text now reads: "A text prompt shows its default as placeholder text in a blank input. The developer presses Tab to insert the default and edit it, presses Enter to accept it, or types another value." Let me know if Enter-to-accept is something we should not advertise.
| - A text prompt with a noun and colon: "App name:" | ||
| - A selection prompt followed by a colon: "Select extension type:" | ||
|
|
||
| Do not require an interactive prompt for automated use. Follow the [JSON output contracts](../../cli/json-output.md#preserve-compatibility) for the independent roles of output format and interactivity. |
There was a problem hiding this comment.
I'd phrase this a bit differently, as anything that potentially requires a prompts must be specifiable via a flag for non-interactive cases.
There was a problem hiding this comment.
Updated in 510d8a7.
Rephrased: "Anything that can require a prompt must also be specifiable with a flag, so non-interactive runs, such as CI and scripts, can supply it." The link also pointed at an anchor that #8754 removed. It now points to json-output.md#conventions-and-changesets, which covers --json vs --no-input.
| | package manager | CLI (always Shopify) | Topic | Command | Argument | Flags (with or without options) | | ||
| | :------------- | :------------- | :------------- | :------------- |:------------- |:------------- | | ||
| | yarn | Shopify | app | generate | extension | --type checkout_ui | ||
| A global installation is available across projects. A local installation belongs to one project or directory. For a project-local installation, use that project's package-manager invocation. For example, a project with a local Shopify CLI can use: |
There was a problem hiding this comment.
We can probably trim all this a bunch, given we're fully into global these days.
There was a problem hiding this comment.
Updated in 510d8a7.
Trimmed. The section now only says that examples use a global installation.
| ### Topics | ||
|
|
||
| ## Topics | ||
| Create a topic only when you add an entirely new domain to the CLI. Get maintainer input before you add one. Domain topics include `app` and `theme`. Hydrogen commands are supplied by a separate plugin; see the [architecture guide](../cli/architecture.md). |
There was a problem hiding this comment.
and store and organization now!
There was a problem hiding this comment.
Updated in 510d8a7.
Added store and organization to the domain topics.
| | ✅ | Do: | pnpm add <package> --ignore-workspace-root-check | This flag is long, but it accurately describes the choice the developer is making. | | ||
| | :------------- | :------------- | :------------- | :------------- | | ||
| | ❌ | Don't: | rsync --owner | Because it’s unnecessarily terse, it’s ambiguous whether this flag means “preserve the current owner” or “assign ownership”.| | ||
| Do not shorten them to `--sync` and `--reload`: those names remove context. They are not supported alternatives for this command. |
There was a problem hiding this comment.
This is phrased as a concrete instruction instead of the real intent, which is an example.
There was a problem hiding this comment.
Updated in 510d8a7.
Reworded as an example: "Shorter names, such as --sync or --reload, would not say what is synced or reloaded."
| ### Options | ||
|
|
||
| By default, two-word options are formatted with hyphens but should also accept underscores. | ||
| A flag can accept specific values, called options. Accept a space or an equals sign between a flag and its value: |
There was a problem hiding this comment.
Accept a space or an equals sign between a flag and its value isn't the responsibility of a maintainer, it's part of oclif default behavior.
There was a problem hiding this comment.
Updated in 510d8a7.
Agreed. It now describes oclif's behavior ("oclif accepts a space or an equals sign…") instead of a maintainer task.
…cli-20261002 # Conflicts: # AGENTS.md # docs/cli-kit/command-guidelines.md
|
Thanks for the review, Ariel. I replied in each thread. Other changes in this update:
Happy to split d3b674e into a separate PR if you prefer. |
amcaplan
left a comment
There was a problem hiding this comment.
One additional comment. Sorry I missed it earlier. I trust you'll address before merging.
| shopify theme dev --live-reload=full-page | ||
| ``` | ||
|
|
||
| For new multi-word option values, use hyphens by default and accept underscore aliases where appropriate. Define and document those aliases explicitly. Existing commands' accepted-value contracts remain authoritative: do not assume an underscore spelling works for every existing option. |
There was a problem hiding this comment.
and accept underscore aliases where appropriate
Actually, I'm realizing we don't seem to accept underscores anywhere. So we should probably just indicate that it's hyphens or bust.

WHY are these changes introduced?
Contributors and coding agents need CLI design guidance that does not depend on internal documentation or design files. Shared guidance helps new commands and flows stay consistent with the rest of Shopify CLI.
WHAT is this pull request doing?
Adapt the internal CLI design article into public contributor guidance and route coding agents to its design, syntax, and UI rules. Keep command outcomes and flows separate from the existing syntax and UI content rules. Replace private references and screenshots with standalone text, and use the existing JSON, error, and UI contracts.
Preserve supported command defaults and accepted option values. This changes contributor docs, not CLI behavior, so no changeset is needed.
How to manually test your changes?
Checklist
patchfor bug fixes ·minorfor new features ·majorfor breaking changes) and added a changeset withpnpm changeset add