Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -858,7 +858,7 @@ npm view @github/copilot 2>&1 | cat | tee output.txt
```

**Prevention layers:**
1. **Compiler sanitization**: The workflow compiler (`pkg/workflow/compiler_yaml.go`) automatically strips ANSI codes from descriptions, sources, and comments using `stringutil.StripANSIEscapeCodes()`
1. **Compiler sanitization**: The workflow compiler (`pkg/workflow/compiler_yaml.go`) automatically strips ANSI codes from descriptions, sources, and comments using `stringutil.StripANSI()`
2. **CI validation**: The `validate-yaml` job in `.github/workflows/ci.yml` scans all YAML files for ANSI escape sequences before other jobs run
3. **Detection command**: Run `find .github/workflows -name "*.yml" -o -name "*.yaml" | xargs grep -P '\x1b\[[0-9;]*[a-zA-Z]'` to check for ANSI codes

Expand Down
231 changes: 231 additions & 0 deletions pkg/console/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,10 @@ All `Format*` functions return a styled string ready to be printed to `os.Stderr
| `FormatPromptMessage(message string) string` | Cyan | Interactive prompt labels |
| `FormatVerboseMessage(message string) string` | Muted/comment | Verbose/debug detail |
| `FormatListItem(item string) string` | Foreground | Individual list entries |
| `FormatListHeader(header string) string` | Plain (WASM only) | Section headers inside lists |
| `FormatSectionHeader(header string) string` | Bold, bordered | Section titles in output |
| `FormatLocationMessage(message string) string` | Foreground (WASM only) | File and location paths |
| `FormatCountMessage(message string) string` | Foreground (WASM only) | Counts and metrics |

### Usage Pattern

Expand Down Expand Up @@ -414,6 +417,21 @@ table := console.RenderTable(console.TableConfig{
fmt.Print(table)
```

### `RenderTree(root TreeNode) string`

Renders a `TreeNode` hierarchy as an indented tree string.

```go
Comment on lines +423 to +424
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

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

RenderTree is only defined under //go:build js || wasm (see pkg/console/console_wasm.go), so it isn’t available in non-WASM builds. The README section should explicitly note this (or document the non-WASM equivalent, if any).

Suggested change
```go
**Note:** `RenderTree` is only available in `js/wasm` builds (it is defined behind a build tag), so it cannot be used from non-WASM targets.
```go
// Available only when building for GOOS=js / GOARCH=wasm.

Copilot uses AI. Check for mistakes.
tree := console.RenderTree(console.TreeNode{
Value: "root",
Children: []console.TreeNode{
{Value: "child1"},
{Value: "child2", Children: []console.TreeNode{{Value: "grandchild"}}},
},
})
fmt.Print(tree)
```

## Types

### `CompilerError`
Expand Down Expand Up @@ -513,6 +531,183 @@ if err != nil || !confirmed {

> **Note**: `ConfirmAction` is only available in non-WASM builds. In WASM environments the function is unavailable.

### `ShowInteractiveList(title string, items []ListItem) (string, error)`

Displays an interactive single-selection list using arrow key navigation. Returns the value of the selected `ListItem`. Returns an error if the list is empty or the prompt is cancelled. Falls back to a numbered text list in non-TTY environments.

Comment on lines +534 to +537
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

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

ShowInteractiveList has a WASM stub that always returns an error (pkg/console/list_wasm.go), but this doc block describes interactive behavior and a non-TTY fallback without mentioning the WASM behavior. Please document that WASM builds return an error / the function is unavailable there, to avoid misleading consumers.

Copilot uses AI. Check for mistakes.
```go
items := []console.ListItem{
console.NewListItem("Option A", "First choice", "a"),
console.NewListItem("Option B", "Second choice", "b"),
}
selected, err := console.ShowInteractiveList("Pick one", items)
```

### `PromptInput(title, description, placeholder string) (string, error)`

Displays a single-line text input prompt. Returns the entered string or an error.

```go
value, err := console.PromptInput("Repository name", "Enter the full owner/repo", "owner/repo")
```

> **Note**: `PromptInput` is only available in WASM builds. Use `huh` forms directly in non-WASM builds.

Comment on lines +546 to +555
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

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

This section says PromptInput is “only available in WASM builds”, but the WASM implementation (pkg/console/input_wasm.go) always returns an error (interactive input not available in Wasm) and there is no non-WASM implementation. The README should reflect the actual behavior (currently unimplemented / always errors) and clarify availability across build targets.

This issue also appears on line 566 of the same file.

Copilot uses AI. Check for mistakes.
### `PromptSecretInput(title, description string) (string, error)`

Displays a masked password/secret input prompt. The entered value is hidden as the user types. Returns the entered secret or an error.

```go
token, err := console.PromptSecretInput("GitHub Token", "Enter your personal access token")
```

> **Note**: In non-WASM builds this requires a TTY on stderr; in WASM builds it always returns an error.

### `PromptInputWithValidation(title, description, placeholder string, validate func(string) error) (string, error)`

Like `PromptInput` but accepts a validation function that is called on the entered value before the form is submitted.

```go
value, err := console.PromptInputWithValidation(
"Workflow name", "Must be kebab-case", "my-workflow",
func(s string) error {
if s == "" {
return errors.New("name cannot be empty")
}
return nil
},
)
```

> **Note**: `PromptInputWithValidation` is only available in WASM builds.

### `PromptSelect(title, description string, options []SelectOption) (string, error)`

Displays a single-select dropdown prompt. Returns the `Value` of the chosen `SelectOption` or an error.

```go
opt, err := console.PromptSelect(
"Engine", "Choose an AI engine",
[]console.SelectOption{
{Label: "Copilot", Value: "copilot"},
{Label: "Claude", Value: "claude"},
},
)
```

> **Note**: `PromptSelect` is only available in WASM builds.

### `PromptMultiSelect(title, description string, options []SelectOption, limit int) ([]string, error)`

Displays a multi-select prompt. Returns a slice of selected `Value` strings or an error. Pass `limit ≤ 0` for no selection limit.

```go
selected, err := console.PromptMultiSelect(
"Toolsets", "Choose toolsets to enable",
[]console.SelectOption{
{Label: "Default", Value: "default"},
{Label: "Issues", Value: "issues"},
{Label: "PRs", Value: "pull_requests"},
},
0,
)
```

> **Note**: `PromptMultiSelect` is only available in WASM builds.

Comment on lines +584 to +617
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

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

PromptSelect/PromptMultiSelect are documented as functional interactive prompts, but in WASM builds they always return an error (pkg/console/select_wasm.go) and there are no non-WASM implementations. The README should clarify that these currently aren’t available (or document the supported alternative API).

This issue also appears on line 618 of the same file.

Copilot uses AI. Check for mistakes.
### `RunForm(fields []FormField) error`

Runs a multi-field interactive form defined by a slice of `FormField` values. Populates the `Value` pointer of each field and returns an error if the form is cancelled or fails.

```go
var name, token string
err := console.RunForm([]console.FormField{
{Type: "input", Title: "Name", Value: &name},
{Type: "password", Title: "Token", Value: &token},
})
```

> **Note**: `RunForm` is only available in WASM builds. Use `huh` forms directly in non-WASM builds.

## Layout Functions

These functions compose styled sections for terminal output. They are available only in WASM builds; non-WASM code should use the `RenderTitleBox`, `RenderInfoSection`, and `RenderComposedSections` functions instead.

### `LayoutTitleBox(title string, width int) string`

Returns a title box string of at least `width` characters containing `title`.

```go
header := console.LayoutTitleBox("Audit Report", 60)
fmt.Fprintln(os.Stderr, header)
```

### `LayoutInfoSection(label, value string) string`

Returns a formatted `label: value` line with consistent indentation.

```go
line := console.LayoutInfoSection("Repository", "github/gh-aw")
fmt.Fprintln(os.Stderr, line)
```

### `LayoutEmphasisBox(content string, color any) string`

Returns `content` wrapped in an emphasis box. The `color` parameter is reserved for future use (for example, to pass a `lipgloss.Color` value) and is currently ignored by the WASM implementation.

```go
box := console.LayoutEmphasisBox("Important notice", nil)
fmt.Fprintln(os.Stderr, box)
```

### `LayoutJoinVertical(sections ...string) string`

Joins multiple rendered section strings with newlines.

```go
output := console.LayoutJoinVertical(header, body, footer)
fmt.Fprintln(os.Stderr, output)
```

## Terminal Control Functions

These functions emit ANSI control sequences to manage the terminal display. They are no-ops when stderr is not a TTY.
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

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

The section header says “These functions … are no-ops when stderr is not a TTY”, but ShowWelcomeBanner still prints the banner/description even when stderr isn’t a TTY (only ClearScreen is gated). Please narrow the statement to ClearScreen/ClearLine, or document the actual behavior of ShowWelcomeBanner in non-TTY environments.

Suggested change
These functions emit ANSI control sequences to manage the terminal display. They are no-ops when stderr is not a TTY.
These functions emit ANSI control sequences to manage the terminal display. `ClearScreen` and `ClearLine` are no-ops when stderr is not a TTY.

Copilot uses AI. Check for mistakes.

### `ClearScreen()`

Clears the terminal screen and moves the cursor to the home position.

```go
console.ClearScreen()
```

### `ClearLine()`

Clears the current terminal line (moves to start of line and erases to end).

```go
console.ClearLine()
fmt.Fprint(os.Stderr, "Updated status...")
```

### `ShowWelcomeBanner(description string)`

Clears the screen and displays the `gh aw` welcome banner followed by `description`. Use at the start of interactive commands for a consistent onboarding experience.

```go
console.ShowWelcomeBanner("Let's create your first agentic workflow.")
```

## Verbose Logging

### `LogVerbose(verbose bool, message string)`

Writes `message` as a `FormatVerboseMessage` to `os.Stderr` when `verbose` is `true`. This is a convenience helper that avoids repetitive `if verbose` guards throughout the codebase.

```go
console.LogVerbose(cfg.Verbose, "Loaded 12 workflow files")
```

## Utility Functions

### `FormatFileSize(size int64) string`
Expand All @@ -525,6 +720,42 @@ console.FormatFileSize(1500) // "1.5 KB"
console.FormatFileSize(2_100_000) // "2.0 MB"
```

### `FormatNumber(n int) string`

Formats a large integer as a compact human-readable string using SI suffixes (`k`, `M`, `B`).

```go
console.FormatNumber(0) // "0"
console.FormatNumber(999) // "999"
console.FormatNumber(1500) // "1.50k"
console.FormatNumber(1_200_000) // "1.20M"
```

### `ToRelativePath(path string) string`

Converts an absolute path to a path relative to the current working directory. If the relative path would require traversing parent directories (`..`), the original absolute path is returned unchanged.

```go
// When cwd is /home/user/projects:
console.ToRelativePath("/home/user/projects/workflow.md") // "workflow.md"
console.ToRelativePath("/etc/hosts") // "/etc/hosts"
```

### `FormatErrorWithSuggestions(message string, suggestions []string) string`

Formats an error message followed by a bulleted list of actionable suggestions. Returns an empty suggestions block when `suggestions` is nil or empty.

```go
msg := console.FormatErrorWithSuggestions(
"Unknown engine 'myengine'",
[]string{
"Valid engines are: copilot, claude, codex",
"Check your workflow frontmatter",
},
)
fmt.Fprint(os.Stderr, msg)
```

### `FormatBanner() string`

Returns the `gh aw` ASCII art banner as a styled string.
Expand Down
Loading