diff --git a/cli/azd/AGENTS.md b/cli/azd/AGENTS.md index 1a1efc76ab0..d66de980669 100644 --- a/cli/azd/AGENTS.md +++ b/cli/azd/AGENTS.md @@ -174,9 +174,9 @@ func (a *myAction) Run(ctx context.Context) (*actions.ActionResult, error) { ### 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: +When working on CLI output, terminal UX, spinners, progress states, colors, or prompts, you **MUST read** the relevant style guide before making any changes or recommendations. Reference the right file for the flow you're working on: -๐Ÿ“„ **`cli/azd/docs/style-guidelines/azd-style-guide.md`** (full path from repo root) +๐Ÿ“„ **`cli/azd/docs/style-guidelines/azd-style-guide.md`** โ€” **core azd design patterns** (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`) @@ -185,7 +185,11 @@ This file is the authoritative reference for core azd terminal UX patterns inclu - 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. +๐Ÿ“„ **`cli/azd/docs/style-guidelines/agentic-ux-style-guide.md`** โ€” **agentic (AI / GitHub Copilot) UX patterns** + +Read this file instead when working on the AI-driven / agentic experience (the "Set up with GitHub Copilot (Preview)" flow, `azd agent`, the copilot session runtime, and the `AgentDisplay` renderer). It documents the distinct magenta + glyph visual language for agent identity, tool activity, subagents, and thinking states. + +> **Note**: These are two distinct visual systems. The core guide's patterns are **not enforced on extensions**, but extension developers are encouraged to follow them where applicable for consistency. Do **not** apply core azd status patterns to agentic flows (or vice versa) โ€” pick the guide that matches the flow. Extension-specific UX is documented in `cli/azd/docs/extensions/extensions-style-guide.md`. ### Code Organization @@ -405,7 +409,8 @@ 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/azd-style-guide.md` - Core azd CLI style guide (colors, spinners, progress states, terminal UX) +- `docs/style-guidelines/agentic-ux-style-guide.md` - Agentic (AI / GitHub Copilot) UX patterns (magenta + glyph vocabulary) - `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/agentic-ux-style-guide.md b/cli/azd/docs/style-guidelines/agentic-ux-style-guide.md new file mode 100644 index 00000000000..7b82adf3ebf --- /dev/null +++ b/cli/azd/docs/style-guidelines/agentic-ux-style-guide.md @@ -0,0 +1,104 @@ +# Azure Developer CLI (`azd`) Agentic UX Style Guide + +## Overview + +This guide covers the **agentic (AI / GitHub Copilot) UX patterns** for the Azure Developer CLI. These are a **deliberately distinct visual system** from the core azd flows documented in [azd-style-guide.md](./azd-style-guide.md). + +> **Scope**: This guide is for the AI-driven / agentic experience only. For core azd terminal UX (progress reports, spinners, colors, prompts, responsive list/table layouts) see [azd-style-guide.md](./azd-style-guide.md). For extension-specific UX see [extensions-style-guide.md](../extensions/extensions-style-guide.md). + +> **Do not blend the two systems.** Core azd status colors (green `(โœ“)`, red `(x)`, yellow `(!)`) still mean success/failure/warning inside agentic output, but the agent's own identity, tool activity, and thinking states use the magenta + glyph vocabulary described here. Never use magenta for core-flow status, and never restyle core progress reports with the agentic glyphs. Note that agentic **prompts deliberately reuse the core prompt conventions** (the blue `?` marker); it is only the agent's own status/output that uses the agentic vocabulary. + +## Agentic (AI / GitHub Copilot) UX Patterns + +azd has an **agentic experience** โ€” the AI-driven flow entered when a user picks **"Set up with GitHub Copilot (Preview)"** during `azd init`, and used by the `azd agent` / copilot session runtime. This experience has a **deliberately distinct visual language** from core azd flows. Its signature is **magenta**, which marks AI/agent identity and active AI work. + +> **Do not blend the two systems.** Core azd status colors (green `(โœ“)`, red `(x)`, yellow `(!)`) still mean success/failure/warning inside agentic output, but the agent's own identity, tool activity, and thinking states use the magenta + glyph vocabulary described here. Never use magenta for core-flow status, and never restyle core progress reports with the agentic glyphs. + +- Entry point (init branch): [`cmd/init.go`](../../cmd/init.go) โ€” `promptInitType()` adds the Copilot option and `initAppWithAgent()` runs the flow. +- Agent display/event renderer: [`internal/agent/display.go`](../../internal/agent/display.go) (`AgentDisplay`). +- Branding constant: `DisplayTitle = "GitHub Copilot"` in `internal/agent/copilot/config_keys.go`. + +### Branding & Naming + +- Use **"GitHub Copilot"** for the user-facing name (via the `DisplayTitle` constant โ€” don't hardcode the string). The `azd init` menu item is **"Set up with GitHub Copilot (Preview)"**. +- The experience is in **preview**: the entry flow prints a preview notice (with a link to `https://aka.ms/azd-feature-stages`) and tells users how to change permissions later via `azd copilot consent`. Keep this notice when touching the flow. + +### Magenta: The Agentic Signature Color + +There is **no `WithMagentaFormat` helper** in `pkg/output/colors.go`. The only magenta-mapped helper is `WithHintFormat` (`color.MagentaString`), which is a **shared** hint color used in both core and agentic flows (spinner glyph, prompt hints, next-step messages) โ€” its magenta is *not* exclusive to agentic UX. The agentic **agent-identity** styling is different: it applies magenta **directly** through `color.MagentaString` from `fatih/color` to agent/tool/subagent names and in-progress AI messages. When flagging violations, distinguish shared `WithHintFormat` hints (allowed anywhere) from direct `color.MagentaString` agent-identity styling (agentic only). + +Magenta agent-identity styling semantically means **"this is the AI agent / AI is actively working"**. It is applied to: + +| Element | Rendering | Location | +| ---------------------------- | ----------------------------------------------------- | -------- | +| Spinner glyph | Animated glyph in magenta via `WithHintFormat` | `pkg/ux/spinner.go` | +| Running tool name | `Running ` โ€” tool name magenta | `internal/agent/display.go` (`SessionEventTypeToolExecutionStarted`) | +| Tool completion (verb) | `โœ”๏ธŽ Ran ` โ€” `` magenta for `powershell`/generic tools | `toolVerb()` in `display.go` | +| Failed tool name | `โœ– ` โ€” name magenta (glyph red) | `printToolState()` in `display.go` | +| Subagent banner | `โ—† ` in magenta | `SessionEventTypeSubagentStarted` | +| Subagent completed name | `โœ”๏ธŽ completed` โ€” name magenta (check green) | `SessionEventTypeSubagentCompleted` | +| Init "preparing" message | `Preparing application for Azure deployment...` magenta | `cmd/init.go` | + +Reserve **direct `color.MagentaString`** styling for these agent-identity / active-AI-work cases. Do not magenta-color arbitrary text. (Shared `WithHintFormat` hints are a separate, allowed use of magenta.) + +### Agentic Glyph & Layout Vocabulary + +The agent renderer uses a distinct glyph set (not the core `(โœ“)/(x)/(!)` prefixes): + +| Glyph | Meaning | Color | +| ----- | ------------------------------ | --------------------------------------- | +| `โ—†` | Subagent started | Magenta | +| `โ—‡` | Skill invoked (`โ—‡ Using skill: `) | Cyan (`color.CyanString`); optional gray `from @` | +| `โœ”๏ธŽ` | Tool / subagent succeeded | Green check; label follows (verb or name) | +| `โœ–` | **Tool** failed | Red glyph + magenta tool name; optional error detail in red on a `โ””` line | +| `โœ–` | **Subagent** failed | Entire single line rendered red (`โœ– failed: `) | +| `โ”œ` / `โ””` | Sub-detail / nested tree lines (shell command, MCP args, error) | Gray (`color.HiBlackString`) | + +- **Nested (subagent) tool calls are indented** two spaces to show hierarchy. +- **Assistant messages** are rendered as terminal markdown via `output.WithMarkdown(...)`, not plain text. +- **Reasoning / "thinking"**: `AgentDisplay` initializes the spinner with the text `Thinking...` (overriding the shared spinner's default `Loading...`); streamed reasoning shows above the spinner in **dim gray** (`color.HiBlackString`), truncated to the last five lines. +- **Cancel affordance**: the live canvas shows `Press Ctrl+C to cancel` in gray with a bold `Ctrl+C`. + +### The Agent Display Canvas + +Live agent output is drawn on a `uxlib.Canvas` (`AgentDisplay.Start`) composed, top to bottom, of: recent reasoning (dim gray) โ†’ blank separator โ†’ spinner (magenta glyph + status text) โ†’ sub-detail tree (`โ”œ`/`โ””`, gray) โ†’ `Press Ctrl+C to cancel`. Completed events (`โ—†`/`โ—‡`/`โœ”๏ธŽ`/`โœ–`) are printed above the canvas and persist; the transient spinner/reasoning region is cleared and redrawn. + +### Prompts in Agentic Flows + +Agentic flows **reuse the core prompt component** ([`pkg/ux/prompt.go`](../../pkg/ux/prompt.go)), so user prompts still use the **blue `?`** marker described in [User Inputs](./azd-style-guide.md#user-inputs). **Agent prompts do not use magenta markers.** Magenta is reserved for agent identity and in-progress AI work, not for soliciting input. When adding prompts inside an agentic flow, follow the core User Inputs rules โ€” do not restyle the `?` marker. + +### Agentic Output Examples + +**Thinking / reasoning + spinner** (spinner glyph is the default `|` `/` `-` `\` animation, rendered magenta): + +``` + ...streamed reasoning text (dim gray)... + + / Thinking... + Press Ctrl+C to cancel +``` + +**Tool run and completion:** + +``` +Running powershell go build... +โœ”๏ธŽ Ran powershell +``` + +**Subagent lifecycle:** + +``` +โ—† GitHub Copilot + โ”” Generates Azure app scaffolding +โœ”๏ธŽ GitHub Copilot completed +``` + +> Colors: `โ—†` and the subagent/tool names โ†’ magenta (`color.MagentaString`). `โœ”๏ธŽ` โ†’ green. `โœ–` and error detail โ†’ red. Reasoning, sub-detail (`โ”œ`/`โ””`), and hints โ†’ gray. `โ—‡ Using skill:` โ†’ cyan. Assistant prose โ†’ `WithMarkdown`. + +### Agentic UX Guidelines + +- **Keep the two systems separate.** Don't apply core progress-report prefixes to agent events, and don't apply agentic glyphs/magenta to core commands. +- **Direct magenta = agent identity / active AI work.** Use `color.MagentaString` for agent/tool/subagent names and in-progress AI messages โ€” not for arbitrary text. (The shared `WithHintFormat` hint color is separate and allowed in any flow.) +- **Reuse the shared prompt and spinner components** โ€” don't fork a magenta prompt marker. +- **Preserve the preview notice and consent guidance** in the init entry flow. +- **Render assistant messages as markdown** (`output.WithMarkdown`) so formatting is legible in the terminal. diff --git a/cli/azd/docs/style-guidelines/azd-style-guide.md b/cli/azd/docs/style-guidelines/azd-style-guide.md index 1b79ff10882..3e289898088 100644 --- a/cli/azd/docs/style-guidelines/azd-style-guide.md +++ b/cli/azd/docs/style-guidelines/azd-style-guide.md @@ -4,7 +4,13 @@ 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. +> **This guide covers core azd design patterns**, including core azd flows and the responsive list/table layouts shared across list commands (`azd tool list`, `azd tool check`, `azd extension list`, etc.). These core patterns are **not enforced on extensions**, but extension developers are encouraged to follow them where applicable for consistent, familiar UX. +> +> **Related guides โ€” reference the right file:** +> - **Agentic (AI / GitHub Copilot) UX** โ†’ [agentic-ux-style-guide.md](./agentic-ux-style-guide.md). The AI-driven / agentic experience uses a **deliberately distinct** magenta + glyph visual language. Do not apply core azd patterns to agentic flows, or vice versa. (Note: agentic **prompts deliberately reuse the core prompt conventions** โ€” the blue `?` marker; only the agent's own status/output uses the agentic vocabulary.) +> - **Extension-specific UX** โ†’ [extensions-style-guide.md](../extensions/extensions-style-guide.md). +> +> Core azd and agentic styling are distinct visual systems โ€” do not mix their conventions (for example, don't use the agentic magenta *agent-identity* styling in core flows, and don't restyle core progress reports with the agentic glyphs). ## Code Style Guidelines @@ -297,6 +303,124 @@ Once the user makes their selection: > Colors: `?` โ†’ `WithHighLightFormat` + `WithBold`. `(US) East US 2 (eastus2)` (selected value) โ†’ `WithHighLightFormat`. +### Responsive List & Table Layouts + +Commands that print a list of items โ€” `azd tool list`, `azd tool check`, `azd extension list` (and its alias `azd ext list`), `azd extension source list`, `azd template list`, `azd template source list`, `azd copilot consent list` โ€” render through the shared responsive formatter rather than hand-rolling their own `tabwriter` output. This keeps column names, colors, and layout behavior consistent, and gives every list command a layout that adapts to the terminal width. + +**Any new or migrated list command MUST use this formatter.** A few legacy commands (e.g. `azd env list`, which still uses the plain `TableFormatter`) predate it โ€” migrate them when touched rather than adding another bespoke table. + +The shared component is `output.PrettyTableFormatter`, configured via `output.PrettyTableFormatterOptions`. + +- Formatter & breakpoint logic: [`pkg/output/pretty_table.go`](../../pkg/output/pretty_table.go) +- Column & option types: [`pkg/output/pretty_table_types.go`](../../pkg/output/pretty_table_types.go) +- Table rendering: [`pkg/output/pretty_table_table.go`](../../pkg/output/pretty_table_table.go) +- Card rendering: [`pkg/output/pretty_table_cards.go`](../../pkg/output/pretty_table_cards.go) +- Width/layout math: [`pkg/output/pretty_table_layout.go`](../../pkg/output/pretty_table_layout.go) + +> Reference implementations: `azd tool list` in [`cmd/tool.go`](../../cmd/tool.go), `azd extension list` in [`cmd/extension.go`](../../cmd/extension.go). Model new list commands on these. + +#### Responsive Breakpoints + +The formatter selects one of three layouts from the current terminal width (`resolveBreakpoint`). Defaults live in `pretty_table_types.go` and can be overridden per command via `FullThreshold` / `CompactThreshold`. + +| Layout | Width condition | What renders | +| ----------- | -------------------------------------- | ------------------------------------------------------------------------ | +| **Full** | `width >= 100` (`DefaultFullThreshold`) | All columns; long values may still wrap/truncate to fit | +| **Compact** | `width >= 60` (`DefaultCompactThreshold`) | Only columns with `Priority <= 2`; the dropped columns collapse into a single header-only `ยทยทยท` marker | +| **Card** | `width < 60` | One card per row, optionally grouped into sections | + +- Do **not** invent new width thresholds per command. Use the shared defaults unless there is a strong, documented reason to override them. +- Set `ForceCards: true` only when a command should always render cards regardless of width. + +#### Defining Columns + +Each column is a `PrettyColumn` (a `Column` plus responsive metadata). Conventions: + +- **Headings are UPPERCASE** and short (`ID`, `NAME`, `STATUS`, `INSTALLED`, `LATEST`, `SOURCE`, `CATEGORY`, `TYPE`, `LOCATION`). Reuse these standard names across commands โ€” don't introduce a synonym like `VERSION` when `INSTALLED`/`LATEST` already express the concept. +- **`Priority`** controls compact visibility: `1` and `2` survive the compact breakpoint; `3+` are full-table only; `0` is treated as `1`. Keep the identifying columns (`ID`, `STATUS`, `INSTALLED`) at priority `1`. +- **`ColorFunc`** applies semantic color to a cell (see status colors below). Use the standard `output.With*Format` helpers, never raw ANSI. +- **`Truncatable`** shortens a value with an ellipsis (`โ€ฆ`), keeping at least `minVisibleChars` (5) characters. +- **`Wrappable`** lets a value wrap onto at most `maxWrapLines` (2) lines before truncating โ€” preferred for identifier columns like `NAME`. +- **`CardTitle`** promotes the column's value to the highlighted (blue, `WithHighLightFormat`) title line of each card (no label), and excludes it from the labeled card fields. Typically set on `NAME`. +- **`CardValueTemplate`** provides an alternate template for the card layout, e.g. to omit a `LATEST` value in cards when it equals `INSTALLED`. (Cards omit empty fields entirely; they do not print a `-` placeholder.) +- **`CardGroupColumn`** (an option, not a column) groups cards under section headers by that column's value (e.g. group by `SOURCE` or `CATEGORY`). Grouping preserves the input order of rows โ€” it does **not** sort; sort your rows before formatting. +- **`ResponsiveColumnHint: true`** enables two related behaviors: the header-only `ยทยทยท` placeholder column (shown only at the compact breakpoint when columns are dropped) **and** the "Showing N of M columnsโ€ฆ" hint line (shown whenever columns are hidden *or* a value is truncated, including in the full layout). Enable it for user-facing lists. + +```go +columns := []output.PrettyColumn{ + {Column: output.Column{Heading: "ID", ValueTemplate: "{{.Id}}"}, Priority: 1}, + { + Column: output.Column{Heading: "NAME", ValueTemplate: "{{.DisplayName}}"}, + Priority: 2, + CardTitle: true, + Wrappable: true, + Truncatable: true, + }, + { + Column: output.Column{Heading: "STATUS", ValueTemplate: "{{.Status}}"}, + Priority: 1, + ColorFunc: extensionStatusColor, // semantic status color + }, +} +formatter.Format(rows, writer, output.PrettyTableFormatterOptions{ + Columns: columns, + CardGroupColumn: "SOURCE", + ResponsiveColumnHint: true, +}) +``` + +#### Header, Spacing & Status Styling + +- **Header row**: bold high-white (`color.New(color.Bold, color.FgHiWhite)`, equivalent to `WithBold`), followed by a **gray** underline rule (`WithGrayFormat`). +- **Column spacing**: a fixed `columnPadding` (3 spaces). **No box-drawing separators** and **no `tabwriter`** in list tables โ€” `tabwriter` is reserved for the `show`/detail screens. +- **Empty values**: in the **table** layouts, render a literal `-` for a missing value (e.g. `INSTALLED` with no installed version). This is done in the column's `ValueTemplate` (e.g. `{{if .Version}}{{.Version}}{{else}}-{{end}}`), not automatically by the formatter. In the **card** layout, empty fields are omitted rather than shown as `-`. +- **Status colors** are centralized in per-command helpers (`toolStatusColor`, `extensionStatusColor`) that map a status string to a color helper. Keep these mappings consistent: + +| Status text | Meaning | Color helper | +| ------------------- | ----------------------------- | ------------------- | +| `Installed` / `Up to date` | Present and current | `WithSuccessFormat` | +| `Upgrade available` | Installed but outdated | `WithWarningFormat` | +| `Incompatible` | Cannot be used | `WithErrorFormat` | +| `Not installed` | Absent (not an error) | `WithGrayFormat` | + +#### Layout Examples + +**Full table** (`width >= 100`) โ€” every column, gray underline rule: + +``` +ID NAME STATUS INSTALLED LATEST SOURCE +โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +az-cli Azure CLI Up to date 1.0.0 1.0.0 azd +``` + +**Compact table** (`60 <= width < 100`) โ€” only `Priority <= 2` columns; the dropped columns collapse into a single header-only `ยทยทยท` column (data rows leave it blank), with a hint line: + +``` +ID NAME STATUS INSTALLED ยทยทยท +โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +az-cli Azure CLI Up to date 1.0.0 + +Showing 4 of 5 columns. Resize the terminal or run with -o json for full details. +``` + +**Card layout** (`width < 60`) โ€” one card per row, grouped by `CardGroupColumn`, `CardTitle` value as the heading: + +``` +โ”€โ”€ SOURCE: azd โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +Azure CLI +ID: az-cli +STATUS: Up to date +INSTALLED: 1.0.0 +``` + +#### List Command Guidelines + +- **Always support `-o json`.** The responsive layouts are for humans; scripts and full detail should use `--output json`. The compact hint explicitly points users there. +- **Empty state**: print a clear, single-line message instead of an empty table (e.g. `No tools found in the registry.`, `No tools found.`). Extension list adds guidance on how to add a source. +- **Stable ordering**: the formatter does not sort โ€” it renders (and groups cards) in the order rows are passed in. **Sort your rows before formatting** and group by a stable key (`tool list` by `CATEGORY`, `extension list` by `SOURCE`) so output is deterministic. +- **Don't hand-roll a new table.** If you find yourself writing `tabwriter` for a list of items, use `PrettyTableFormatter` instead so the command inherits responsive behavior for free. + ### CLI Color Standards The CLI uses consistent color formatting through helper functions defined in [`cli/azd/pkg/output/colors.go`](../../pkg/output/colors.go). **Always use these helper functions** instead of writing raw ANSI escape codes.