Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
44 changes: 44 additions & 0 deletions cli/azd/extensions/azure.ai.agents/internal/cmd/banner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package cmd

import (
"fmt"
"io"
"strings"

"azureaiagent/internal/version"

"github.com/fatih/color"
)

// ASCII art using ANSI Shadow font for "FOUNDRY".
// Visual width is 61 columns; each box-drawing character is one display column
// but occupies multiple UTF-8 bytes, so len() over-counts. Tests use
// rune-aware width measurement.
const bannerArt = `███████╗ ██████╗ ██╗ ██╗███╗ ██╗██████╗ ██████╗ ██╗ ██╗
██╔════╝██╔═══██╗██║ ██║████╗ ██║██╔══██╗██╔══██╗╚██╗ ██╔╝
█████╗ ██║ ██║██║ ██║██╔██╗ ██║██║ ██║██████╔╝ ╚████╔╝
██╔══╝ ██║ ██║██║ ██║██║╚██╗██║██║ ██║██╔══██╗ ╚██╔╝
██║ ╚██████╔╝╚██████╔╝██║ ╚████║██████╔╝██║ ██║ ██║
╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═══╝╚═════╝ ╚═╝ ╚═╝ ╚═╝
`

// printBanner renders the styled ASCII art banner to w.
func printBanner(w io.Writer) {
purple := color.RGB(109, 53, 255).Add(color.Bold)
dim := color.New(color.Faint)
fmt.Fprintln(w)

for _, line := range strings.Split(bannerArt, "\n") {
purple.Fprintln(w, line)

Check failure on line 35 in cli/azd/extensions/azure.ai.agents/internal/cmd/banner.go

View workflow job for this annotation

GitHub Actions / lint / golangci-lint (ubuntu-latest)

G104: Errors unhandled (gosec)
}

// Tagline + version on one line
dim.Fprintf(w, "v%s", version.Version)

Check failure on line 39 in cli/azd/extensions/azure.ai.agents/internal/cmd/banner.go

View workflow job for this annotation

GitHub Actions / lint / golangci-lint (ubuntu-latest)

G104: Errors unhandled (gosec)
fmt.Fprint(w, " ")
fmt.Fprintln(w)
Comment thread
therealjohn marked this conversation as resolved.
Outdated
dim.Fprintln(w, "Visit the docs at https://aka.ms/azd-ai-agent-docs")

Check failure on line 42 in cli/azd/extensions/azure.ai.agents/internal/cmd/banner.go

View workflow job for this annotation

GitHub Actions / lint / golangci-lint (ubuntu-latest)

G104: Errors unhandled (gosec)
fmt.Fprintln(w)
}
71 changes: 71 additions & 0 deletions cli/azd/extensions/azure.ai.agents/internal/cmd/banner_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package cmd

import (
"bytes"
"strings"
"testing"
"unicode/utf8"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// displayWidth returns the number of runes (visual columns) in s.
// Box-drawing and block characters are each one column wide, but multi-byte
// in UTF-8, so we count runes instead of bytes.
func displayWidth(s string) int {
return utf8.RuneCountInString(s)
}

func TestBannerFitsWithin100Columns(t *testing.T) {
for i, line := range strings.Split(bannerArt, "\n") {
w := displayWidth(line)
assert.LessOrEqualf(t, w, 100,
"banner line %d exceeds 100 columns (%d runes): %q", i+1, w, line)
}
}

func TestPrintBannerWritesOutput(t *testing.T) {
var buf bytes.Buffer
printBanner(&buf)

output := buf.String()
require.NotEmpty(t, output, "printBanner should produce output when writing to a buffer")
assert.Contains(t, output, "██", "banner should contain block-drawing characters")
assert.Contains(t, output, "https://aka.ms/azd-ai-agent-docs", "banner should contain the docs link")
}

func TestPrintBannerAllLinesFit100Cols(t *testing.T) {
var buf bytes.Buffer
printBanner(&buf)

for i, line := range strings.Split(buf.String(), "\n") {
clean := stripAnsi(line)
w := displayWidth(clean)
assert.LessOrEqualf(t, w, 100,
"rendered line %d exceeds 100 columns (%d runes): %q", i+1, w, clean)
}
}

// stripAnsi removes ANSI escape sequences from a string.
func stripAnsi(s string) string {
var out strings.Builder
inEsc := false
for _, r := range s {
if r == '\x1b' {
inEsc = true
continue
}
if inEsc {
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') {
inEsc = false
}
continue
}
out.WriteRune(r)
}
return out.String()
}
4 changes: 2 additions & 2 deletions cli/azd/extensions/azure.ai.agents/internal/cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ func newInitCommand(rootFlags *rootFlagsDefinition) *cobra.Command {
Short: fmt.Sprintf("Initialize a new AI agent project. %s", color.YellowString("(Preview)")),
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
printBanner(os.Stdout)
Comment thread
therealjohn marked this conversation as resolved.
Outdated

ctx := azdext.WithAccessToken(cmd.Context())

setupDebugLogging(cmd.Flags())
Expand Down Expand Up @@ -236,8 +238,6 @@ func newInitCommand(rootFlags *rootFlagsDefinition) *cobra.Command {
}

func (a *InitAction) Run(ctx context.Context) error {
color.Green("Initializing AI agent project...")
fmt.Println()

// If src path is absolute, convert it to relative path compared to the azd project path
if a.flags.src != "" && filepath.IsAbs(a.flags.src) {
Expand Down
11 changes: 10 additions & 1 deletion cli/azd/extensions/azure.ai.agents/internal/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,23 @@ var rootFlags rootFlagsDefinition
func NewRootCommand() *cobra.Command {
rootCmd := &cobra.Command{
Use: "agent <command> [options]",
Short: fmt.Sprintf("Extension for the Foundry Agent Service. %s", color.YellowString("(Preview)")),
Short: fmt.Sprintf("Ship agents with Microsoft Foundry from your terminal. %s", color.YellowString("(Preview)")),
Comment thread
therealjohn marked this conversation as resolved.
SilenceUsage: true,
SilenceErrors: true,
CompletionOptions: cobra.CompletionOptions{
DisableDefaultCmd: true,
},
}

// Show the ASCII art banner above the default help text for the root command
defaultHelp := rootCmd.HelpFunc()
rootCmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
if cmd == rootCmd {
printBanner(cmd.OutOrStdout())
}
defaultHelp(cmd, args)
})

rootCmd.SetHelpCommand(&cobra.Command{Hidden: true})
rootCmd.PersistentFlags().BoolVar(
&rootFlags.Debug,
Expand Down
Loading