diff --git a/cli/azd/AGENTS.md b/cli/azd/AGENTS.md index a95ed63e5a7..0807174d8d0 100644 --- a/cli/azd/AGENTS.md +++ b/cli/azd/AGENTS.md @@ -157,6 +157,21 @@ func (a *myAction) Run(ctx context.Context) (*actions.ActionResult, error) { - **Shell-safe output**: When emitting shell commands in user-facing messages (e.g., `cd `), quote paths that may contain spaces. Use `fmt.Sprintf("cd %q", path)` or conditionally wrap in quotes - **Consistent JSON types**: When adding fields to JSON output (`--output json`), match the types used by similar fields across commands. Don't mix `*time.Time` and custom timestamp wrappers (e.g., `*RFC3339Time`) in the same API surface +### CLI UX & Style + +When working on CLI output, terminal UX, spinners, progress states, colors, or prompts for **core azd flows**, you **MUST read** the style guide before making any changes or recommendations: + +📄 **`cli/azd/docs/style-guidelines/azd-style-guide.md`** (full path from repo root) + +This file is the authoritative reference for core azd terminal UX patterns including: +- Progress report states (`(✓) Done`, `(x) Failed`, `(!) Warning`, `(-) Skipped`) +- Spinner type (bar-fill `|=======|`) +- Color conventions (`WithSuccessFormat`, `WithErrorFormat`, `WithHighLightFormat`, etc.) +- User input patterns (text input, list select, yes/no confirm) +- Prompt styling (`?` marker in bold blue, `[Type ? for hint]`, post-submit states) + +> **Note**: This style guide covers **core azd flows only**. Separate guidelines for agentic flows and extension-specific UX will be provided in dedicated files in the future. Do not apply core azd patterns to agentic or extension flows without a dedicated style reference. + ### Code Organization - **Import order**: stdlib → external → azure/azd internal → local @@ -307,6 +322,7 @@ go build Feature-specific docs are in `docs/` — refer to them as needed. Some key docs include: +- `docs/style-guidelines/azd-style-guide.md` - CLI style guide (colors, spinners, progress states, terminal UX) - `docs/style-guidelines/new-azd-command.md` - Adding new commands - `docs/extensions/extension-framework.md` - Extension development using gRPC extension framework - `docs/style-guidelines/guiding-principles.md` - Design principles diff --git a/cli/azd/docs/style-guidelines/azd-style-guide.md b/cli/azd/docs/style-guidelines/azd-style-guide.md index da193e358a8..e7de0497819 100644 --- a/cli/azd/docs/style-guidelines/azd-style-guide.md +++ b/cli/azd/docs/style-guidelines/azd-style-guide.md @@ -4,6 +4,8 @@ This style guide establishes standards for code, user experience, testing, and documentation across the Azure Developer CLI project. Following these guidelines ensures consistency, maintainability, and a high-quality user experience. +> **Scope**: This guide covers **core azd flows** only. Separate guidelines for agentic flows and extension-specific UX will be provided in dedicated files in the future. + ## Code Style Guidelines ### Go Conventions @@ -70,7 +72,7 @@ func (s *Service) Run(ctx context.Context) error { ### Progress Reports -Progress reports provide real-time feedback during multi-step operations. Use progress reports to: +Progress reports provide real-time feedback during a single command's execution, showing the status of individual steps as they happen. Use progress reports to: - Show status of long-running commands - Display individual service provisioning/deployment states @@ -81,79 +83,214 @@ Progress reports provide real-time feedback during multi-step operations. Use pr Items in a progress report list can be in one of five states: 1. **Loading**: `|=== | [Verb] Message goes here` - - Indicates operation in progress - - Shows loading bar animation + - Indicates operation in progress; animated bar-fill spinner in gray (`WithGrayFormat`) 2. **Done**: `(✓) Done: [Verb] Message goes here` - - Green checkmark indicates successful completion - - Past tense verb + - Green checkmark via `WithSuccessFormat`; use past tense verb -3. **Failed**: `(✗) Failed: [Verb] Message goes here` - - Red X indicates error - - Include specific error message below +3. **Failed**: `(x) Failed: [Verb] Message goes here` + - Red `x` via `WithErrorFormat`; include specific error message below 4. **Warning**: `(!) Warning: [Verb] Message goes here` - - Yellow exclamation for non-blocking issues - - Command continues but user should be aware + - Yellow exclamation via `WithWarningFormat`; non-blocking, command continues 5. **Skipped**: `(-) Skipped: [Verb] Message goes here` - - Gray dash indicates intentionally skipped step - - Different from failed - this is expected behavior + - Gray dash via `WithGrayFormat`; intentionally skipped (not a failure) + +#### Quick Reference + +**Progress report state → prefix → color function:** + +| State | Prefix | Color Function | Notes | +| -------- | -------------- | ------------------- | ------------------------- | +| Loading | `\|=== \|` | `WithGrayFormat` | Animated bar-fill spinner | +| Done | `(✓) Done:` | `WithSuccessFormat` | Past tense verb | +| Failed | `(x) Failed:` | `WithErrorFormat` | Follow with error detail | +| Warning | `(!) Warning:` | `WithWarningFormat` | Non-blocking issue | +| Skipped | `(-) Skipped:` | `WithGrayFormat` | Expected, not a failure | + +**Log type → prefix → color function:** + +| Log Type | Prefix | Color Function | When to use | +| ---------- | ----------------- | ------------------- | ------------------------------------------ | +| Success | `SUCCESS:` | `WithSuccessFormat` | Command completed its primary goal | +| Error | `ERROR:` | `WithErrorFormat` | Command failed; top-level failure message | +| Warning | `WARNING:` | `WithWarningFormat` | Non-fatal issue the user should know about | #### Progress Report Guidelines - **Indentation**: Progress items are always indented under the main command -- **Hierarchy**: Sub-steps appear indented under parent steps - **Verb consistency**: Use present progressive for loading, past tense for completed - **Contextual information**: Include resource names, identifiers when relevant -#### Examples +#### Example -**Success scenario:** +**Failure scenario** (shows how multiple states compose with error detail): ``` Provisioning Azure resources (azd provision) -Provisioning Azure resources can take some time. (✓) Done: Creating App Service Plan: plan-r2w2adrz3rvwxu (✓) Done: Creating Log Analytics workspace: log-r2w2adrz3rvwxu - (✓) Done: Creating Application Insights: appi-r2w2adrz3rvwxu - (✓) Done: Creating App Service: app-api-r2w2adrz3rvwxu + (x) Failed: Creating Cosmos DB: cosmos-r2w2adrz3rvwxu + The '{US} West US 2 (westus)' region is currently experiencing high demand + and cannot fulfill your request. Failed to create Cosmos DB account. + +ERROR: Unable to complete provisioning of Azure resources, 'azd up' failed ``` -**Failure scenario:** +> Colors: Title → `WithBold`. `(✓) Done:` → `WithSuccessFormat`. `(x) Failed:` and error detail → `WithErrorFormat`. `ERROR:` line → `WithErrorFormat`. + +### Success / Error / Warning Logs + +These logs represent the final outcome of a command, displayed after execution completes. Standard logs (Success, Error, Warning) use an **all-caps prefix** followed by a colon to separate the log type from the message. + +All logs should: + +- Be **succinct** +- Be **human legible** +- **Provide a path forward** when possible + +There are some specific edge cases where the log prefix is not required. + +#### When to Use Each Log Type + +- **SUCCESS**: The command achieved its primary goal. Use after deployments, provisioning, or any command that completes a user-requested action. +- **ERROR**: The command failed and cannot continue. Always include the reason and, when possible, a suggested fix. Only use for top-level command failures, not internal retries. +- **WARNING**: The command succeeded (or will proceed), but something unexpected happened that the user should know about. Examples: deprecated flags, region capacity concerns, overwriting existing resources. + +#### Log Format ``` -Provisioning Azure resources (azd provision) +PREFIX: Message goes here. +``` - (✓) Done: Creating App Service Plan: plan-r2w2adrz3rvwxu - (✓) Done: Creating Log Analytics workspace: log-r2w2adrz3rvwxu - (✗) Failed: Creating Cosmos DB: cosmos-r2w2adrz3rvwxu - - The '{US} West US 2 (westus)' region is currently experiencing high demand - and cannot fulfill your request. Failed to create Cosmos DB account. +#### Log Types + +**Success logs** — Use `WithSuccessFormat` (green/bright green). -ERROR: Unable to complete provisioning of Azure resources, 'azd up' failed +``` +SUCCESS: Message goes here. ``` -**Skipped scenario:** +**Error logs** — Use `WithErrorFormat` (red/bright red). Follow a `[Reason] [Outcome]` structure when possible. ``` -Note: Steps were skipped because _____ (directory, or specified service name) -If you want to deploy all services, you can run azd deploy --all or -move to root directory. +ERROR: Message goes here. +``` - (✓) Done: [Verb] Message goes here - (✓) Done: [Verb] Message goes here - (-) Skipped: [Verb] Message goes here +**Warning logs** — Use `WithWarningFormat` (yellow/bright yellow). + +``` +WARNING: Message goes here. ``` -### Command Output Standards +#### In-Context Example + +**Error with suggested command** (shows multi-line composition with inline highlights): + +``` +ERROR: 'todo-mongojs' is misspelled or missing a recognized flag. +Run azd up --help to see all available flags. +``` + +> Colors: `ERROR:` line → `WithErrorFormat`. `azd up --help` → `WithHighLightFormat`. + +### User Inputs + +Certain azd commands require the user to input text, select yes/no, or select an item from a list. Examples include: + +- Inputting an environment name (text input) +- Initializing a new Git repository (y/n) +- Selecting a location to deploy to (list select) + +#### General Guidelines + +All input requests are in bold and begin with a blue `?`. This helps ensure that they stand out to users as different from other plain text logs and CLI outputs. + +#### Text Input + +Text input captures a single line of input from the user. + +**Initial state:** + +The prompt is displayed with a `[Type ? for hint]` helper in **hi-blue bold text** via `WithHighLightFormat` (Bright Blue, ANSI 94), followed by ghost-text (secondary text color) as placeholder content. + +``` +? This captures a single line of input: [Type ? for hint] +``` + +> Colors: `?` → `WithHighLightFormat` + `WithBold`. `[Type ? for hint]` → `WithHighLightFormat` + `WithBold`. + +**Hint feature:** -- **Structured output**: Support `--output json` for machine-readable output -- **Progress indicators**: Use spinners and progress bars for long operations -- **Colorization**: Use consistent colors (green=success, red=error, yellow=warning) -- **URLs**: Always include relevant portal/console URLs for created resources +If the user types `?`, a hint line appears below the prompt to provide additional guidance. + +``` +? This captures a single line of input: [Type ? for hint] +Hint: This is a help message +``` + +> Colors: `?` and `[Type ? for hint]` → `WithHighLightFormat` + `WithBold`. + +#### Yes or No Input + +- Yes/no inputs include a `(Y/n)` delineator at the end of the input request (before the colon). +- Users can either input `y`/`n` or hit the return key which will select the capitalized choice in the `(Y/n)` delineator. + +``` +? Do you want to initialize a new Git repository in this directory? (Y/n): +``` + +> Colors: `?` → `WithHighLightFormat` + `WithBold`. + +#### List Select + +The list select pattern presents a list of options for the user to choose from. + +**Initial state:** + +The prompt is displayed with a `Filter:` line showing ghost-text ("Type to filter list") and a footer hint in **hi-blue text** via `WithHighLightFormat` (Bright Blue, ANSI 94). The active selection is indicated by `>` and displayed in bold blue text. + +``` +? Select a single option: [Use arrows to move, type to filter] + + > Option 1 + Option 2 + Option 3 + Option 4 + Option 5 + Option 6 + ... +``` + +> Colors: `?` → `WithHighLightFormat` + `WithBold`. `[Use arrows to move, type to filter]` → `WithHighLightFormat` + `WithBold`. `> Option 1` (selected item) → `WithHighLightFormat` + `WithBold`. + +**Display rules:** + +- The list should display no more than 7 items at a time. +- When a list contains more than 7 items, ellipses (`...`) should be used to help users understand there are more items available up or down the list. +- If possible, the most commonly selected item (or the item we want to guide the user into selecting) should be highlighted by default. +- The active selection in the list should be bold and in blue text, prefixed with `>`. + +**Hint:** + +- The `[Type ? for hint]` pattern follows the same behavior as Text Input — **hi-blue bold** via `WithHighLightFormat` (Bright Blue, ANSI 94). + +**After submitting:** + +Once the user makes their selection: + +- The input changes from primary text to hi-blue text +- The `[Type ? for hint]` helper disappears +- If the hint line was visible, it also disappears +- The list collapses and the selected value is printed in blue text next to the prompt + +``` +? Select an Azure location to use: (US) East US 2 (eastus2) +``` + +> Colors: `?` → `WithHighLightFormat` + `WithBold`. `(US) East US 2 (eastus2)` (selected value) → `WithHighLightFormat`. ### CLI Color Standards @@ -173,7 +310,6 @@ Use these functions for consistent color formatting across the CLI: | **Warning** | `WithWarningFormat(text string)` | Warning messages, non-critical issues | | **Error** | `WithErrorFormat(text string)` | Error messages, failures | | **Gray text** | `WithGrayFormat(text string)` | Secondary information, muted text | -| **Hint text** | `WithHintFormat(text string)` | Helpful suggestions, tips | | **Bold text** | `WithBold(text string)` | Emphasis, headers | | **Underline** | `WithUnderline(text string)` | Emphasis (use sparingly) | @@ -187,7 +323,7 @@ fmt.Println(output.WithSuccessFormat("(✓) Done:") + " Creating resource") fmt.Println(output.WithWarningFormat("(!) Warning:") + " Configuration may need update") // Error message -fmt.Println(output.WithErrorFormat("(✗) Failed:") + " Unable to connect") +fmt.Println(output.WithErrorFormat("(x) Failed:") + " Unable to connect") // Hyperlink fmt.Printf("View in portal: %s\n", output.WithLinkFormat(url)) @@ -204,6 +340,16 @@ fmt.Printf("Run %s to deploy\n", output.WithHighLightFormat("azd deploy")) - **Theme support**: Test in both light and dark terminal themes - **Fallback**: Ensure output remains readable if colors are disabled +#### Common Mistakes + +| ❌ Don't | ✅ Do | Why | +| --- | --- | --- | +| `Error: something went wrong` | `ERROR: something went wrong.` | All-caps prefix is required | +| `Done: Created resource` | `(✓) Done: Created resource` | Include icon prefix for progress states | +| `color.Red("ERROR")` | `output.WithErrorFormat("ERROR:")` | Always use helper functions | +| Print bare success text | `SUCCESS: Your app has been deployed!` | Always include the `SUCCESS:` prefix | +| `(✗) Failed:` (Unicode ballot X) | `(x) Failed:` (lowercase letter x) | Match the codebase symbol | +
📚 Learn more about ANSI color codes (optional reference) @@ -252,6 +398,36 @@ Note: There is a discrepancy in the naming convention between ANSI Color coding
+### Loading Animation (Progress Spinner) + +`azd` uses a **bar-fill spinner** to indicate ongoing operations. The animation displays a pair of `|` border characters with `=` fill characters that bounce back and forth, alongside a status message describing the current operation. + +#### Animation Behavior + +The spinner **animates in place** on a single line by overwriting itself. The `=` fill characters grow and bounce back and forth between the `|` borders, creating a fluid loading effect: + +``` +|===== | Creating App Service: my-app-r2w2adrz3rvwxu +``` + +The full animation cycle frames are: + +``` +| | → |= | → |== | → |=== | → |==== | → |===== | → |====== | → |=======| +|=======| → | ======| → | =====| → | ====| → | ===| → | ==| → | =| → | | +``` + +These frames repeat continuously on the **same line** until the operation completes. The spinner bar is displayed in **gray text** via `WithGrayFormat` to keep focus on the status message. + +#### Implementation + +Use `console.ShowSpinner()` and `console.StopSpinner()` from [`pkg/input/console.go`](../../pkg/input/console.go) to display the bar-fill spinner. These are the standard functions used across all azd commands. + +```go +// Start or update spinner +console.ShowSpinner(ctx, "Creating App Service: my-app-r2w2adrz3rvwxu", input.Step) +``` + ## Testing Standards ### Test Structure