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 cli/azd/extensions/azure.ai.agents/extension.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
id: azure.ai.agents
namespace: ai.agent
displayName: Foundry agents (Preview)
description: Extension for the Foundry Agent Service. (Preview)
description: Ship agents with Microsoft Foundry from your terminal. (Preview)
usage: azd ai agent <command> [options]
# NOTE: Make sure version.txt is in sync with this version.
version: 0.1.16-preview
Expand Down
42 changes: 42 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,42 @@
// 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 = `███████╗ ██████╗ ██╗ ██╗███╗ ██╗██████╗ ██████╗ ██╗ ██╗
██╔════╝██╔═══██╗██║ ██║████╗ ██║██╔══██╗██╔══██╗╚██╗ ██╔╝
█████╗ ██║ ██║██║ ██║██╔██╗ ██║██║ ██║██████╔╝ ╚████╔╝
██╔══╝ ██║ ██║██║ ██║██║╚██╗██║██║ ██║██╔══██╗ ╚██╔╝
██║ ╚██████╔╝╚██████╔╝██║ ╚████║██████╔╝██║ ██║ ██║
╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═══╝╚═════╝ ╚═╝ ╚═╝ ╚═╝
`

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.SplitSeq(bannerArt, "\n") {
purple.Fprintln(w, line) //nolint:gosec // G104 - banner output errors are non-critical
}

dim.Fprintf(w, "v%s", version.Version) //nolint:gosec // G104 - banner output errors are non-critical
fmt.Fprint(w, " ")
fmt.Fprintln(w)
dim.Fprintln(w, "Visit the docs at https://aka.ms/azd-ai-agent-docs") //nolint:gosec // G104 - banner output errors are non-critical
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(cmd.OutOrStdout())

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)")),
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