CLI Examples
Complete examples of CLI agents, dual-mode setups, and custom formatters
Example 1: Simple CLI-Only Agent
A basic CLI tool with no control plane dependency:
package main
import (
"context"
"fmt"
"log"
"github.com/Agent-Field/agentfield/sdk/go/agent"
)
func main() {
a, err := agent.New(agent.Config{
NodeID: "greeter",
CLIConfig: &agent.CLIConfig{
AppName: "greeter",
AppDescription: "Simple greeting tool",
},
})
if err != nil {
log.Fatal(err)
}
a.RegisterReasoner("greet", func(ctx context.Context, input map[string]any) (any, error) {
name, ok := input["name"].(string)
if !ok {
return nil, fmt.Errorf("name is required")
}
return fmt.Sprintf("Hello, %s!", name), nil
},
agent.WithCLI(),
agent.WithDefaultCLI(),
agent.WithDescription("Greet a user by name"),
)
if err := a.Run(context.Background()); err != nil {
log.Fatal(err)
}
}Build and use:
# Build
go build -o greeter
# Use
./greeter --set name=Alice
# Output: Hello, Alice!
# Help
./greeter --helpExample 2: Dual-Mode Agent (CLI + Server)
Same binary runs as CLI tool or control plane agent:
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/Agent-Field/agentfield/sdk/go/agent"
)
func main() {
a, err := agent.New(agent.Config{
NodeID: "processor",
Version: "1.0.0",
AgentFieldURL: os.Getenv("AGENTFIELD_URL"), // Optional
CLIConfig: &agent.CLIConfig{
AppName: "processor",
AppDescription: "Data processor with control plane integration",
HelpEpilog: `Examples:
# CLI mode
$ ./processor --set input="data"
# Server mode
$ AGENTFIELD_URL=http://localhost:8080 ./processor serve`,
},
})
if err != nil {
log.Fatal(err)
}
a.RegisterReasoner("process", func(ctx context.Context, input map[string]any) (any, error) {
data := input["input"].(string)
result := map[string]any{
"processed": fmt.Sprintf("Processed: %s", data),
"length": len(data),
}
return result, nil
},
agent.WithCLI(),
agent.WithDefaultCLI(),
agent.WithDescription("Process input data"),
)
if err := a.Run(context.Background()); err != nil {
log.Fatal(err)
}
}Usage:
# Build once
go build -o processor
# CLI mode (standalone)
./processor --set input="hello world"
# Server mode (with control plane)
AGENTFIELD_URL=http://localhost:8080 ./processor serveExample 3: Multiple CLI Commands
Agent with several CLI commands:
package main
import (
"context"
"fmt"
"log"
"strings"
"github.com/Agent-Field/agentfield/sdk/go/agent"
)
func main() {
a, err := agent.New(agent.Config{
NodeID: "text-tools",
CLIConfig: &agent.CLIConfig{
AppName: "text-tools",
AppDescription: "Text processing utilities",
},
})
if err != nil {
log.Fatal(err)
}
// Default command: analyze
a.RegisterReasoner("analyze", func(ctx context.Context, input map[string]any) (any, error) {
text := input["text"].(string)
return map[string]any{
"length": len(text),
"words": len(strings.Fields(text)),
"lines": len(strings.Split(text, "\n")),
}, nil
},
agent.WithCLI(),
agent.WithDefaultCLI(),
agent.WithDescription("Analyze text statistics"),
)
// Uppercase command
a.RegisterReasoner("uppercase", func(ctx context.Context, input map[string]any) (any, error) {
text := input["text"].(string)
return strings.ToUpper(text), nil
},
agent.WithCLI(),
agent.WithDescription("Convert text to uppercase"),
)
// Reverse command
a.RegisterReasoner("reverse", func(ctx context.Context, input map[string]any) (any, error) {
text := input["text"].(string)
runes := []rune(text)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes), nil
},
agent.WithCLI(),
agent.WithDescription("Reverse text"),
)
if err := a.Run(context.Background()); err != nil {
log.Fatal(err)
}
}Usage:
# Default command (analyze)
./text-tools --set text="Hello World"
# Explicit commands
./text-tools uppercase --set text="hello"
./text-tools reverse --set text="hello"
# List commands
./text-tools --helpExample 4: Custom Formatter
Pretty output with custom formatting:
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/Agent-Field/agentfield/sdk/go/agent"
"github.com/fatih/color"
)
func main() {
a, err := agent.New(agent.Config{
NodeID: "analyzer",
CLIConfig: &agent.CLIConfig{
AppName: "analyzer",
AppDescription: "Document analyzer",
DefaultOutputFormat: "text",
},
})
if err != nil {
log.Fatal(err)
}
a.RegisterReasoner("analyze", func(ctx context.Context, input map[string]any) (any, error) {
text := input["text"].(string)
sentiment := "positive"
if len(text) < 10 {
sentiment = "neutral"
}
return map[string]any{
"sentiment": sentiment,
"confidence": 0.85,
"length": len(text),
"topics": []string{"technology", "innovation"},
}, nil
},
agent.WithCLI(),
agent.WithDefaultCLI(),
agent.WithCLIFormatter(func(ctx context.Context, result any, err error) {
if err != nil {
color.Red("❌ Error: %v", err)
os.Exit(1)
return
}
data := result.(map[string]any)
fmt.Println()
color.Cyan("📊 Analysis Results")
color.Cyan("══════════════════")
fmt.Println()
// Sentiment with color
sentiment := data["sentiment"].(string)
fmt.Print("Sentiment: ")
switch sentiment {
case "positive":
color.Green(sentiment)
case "negative":
color.Red(sentiment)
default:
color.Yellow(sentiment)
}
// Confidence
confidence := data["confidence"].(float64)
fmt.Printf("Confidence: %.1f%%\n", confidence*100)
// Length
length := data["length"].(int)
fmt.Printf("Length: %d characters\n", length)
// Topics
topics := data["topics"].([]string)
fmt.Printf("Topics: %v\n", topics)
fmt.Println()
}),
)
if err := a.Run(context.Background()); err != nil {
log.Fatal(err)
}
}Output:
📊 Analysis Results
══════════════════
Sentiment: positive
Confidence: 85.0%
Length: 156 characters
Topics: [technology innovation]Example 5: AI-Powered CLI Tool
CLI tool with AI capabilities:
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/Agent-Field/agentfield/sdk/go/agent"
"github.com/Agent-Field/agentfield/sdk/go/ai"
)
func main() {
a, err := agent.New(agent.Config{
NodeID: "ai-assistant",
AIConfig: &ai.Config{
Provider: "openai",
APIKey: os.Getenv("OPENAI_API_KEY"),
Model: "gpt-4",
},
CLIConfig: &agent.CLIConfig{
AppName: "ai-assistant",
AppDescription: "AI-powered CLI assistant",
HelpPreamble: "⚠️ Set OPENAI_API_KEY before running",
EnvironmentVars: []string{
"OPENAI_API_KEY (required) - Your OpenAI API key",
},
},
})
if err != nil {
log.Fatal(err)
}
a.RegisterReasoner("ask", func(ctx context.Context, input map[string]any) (any, error) {
question := input["question"].(string)
// Use AI client
resp, err := a.AI(ctx, question)
if err != nil {
return nil, fmt.Errorf("AI request failed: %w", err)
}
return resp.Choices[0].Message.Content, nil
},
agent.WithCLI(),
agent.WithDefaultCLI(),
agent.WithDescription("Ask the AI a question"),
agent.WithCLIFormatter(func(ctx context.Context, result any, err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
return
}
fmt.Println(result)
}),
)
if err := a.Run(context.Background()); err != nil {
log.Fatal(err)
}
}Usage:
export OPENAI_API_KEY=sk-...
./ai-assistant --set question="Explain quantum computing"Example 6: File Processing
Process files with progress indication:
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/Agent-Field/agentfield/sdk/go/agent"
)
func main() {
a, err := agent.New(agent.Config{
NodeID: "file-processor",
CLIConfig: &agent.CLIConfig{
AppName: "file-processor",
AppDescription: "Process files with AI",
},
})
if err != nil {
log.Fatal(err)
}
a.RegisterReasoner("process", func(ctx context.Context, input map[string]any) (any, error) {
filePath := input["file"].(string)
// Read file
data, err := os.ReadFile(filePath)
if err != nil {
return nil, fmt.Errorf("failed to read file: %w", err)
}
// Process
result := map[string]any{
"file": filePath,
"size": len(data),
"lines": len(data),
}
return result, nil
},
agent.WithCLI(),
agent.WithDefaultCLI(),
agent.WithDescription("Process a file"),
)
if err := a.Run(context.Background()); err != nil {
log.Fatal(err)
}
}Usage:
./file-processor --set file=document.txtExample 7: JSON Input/Output
Machine-readable CLI for pipelines:
package main
import (
"context"
"fmt"
"log"
"strings"
"github.com/Agent-Field/agentfield/sdk/go/agent"
)
func main() {
a, err := agent.New(agent.Config{
NodeID: "filter",
CLIConfig: &agent.CLIConfig{
AppName: "filter",
AppDescription: "Filter and transform JSON data",
DefaultOutputFormat: "json",
},
})
if err != nil {
log.Fatal(err)
}
a.RegisterReasoner("filter", func(ctx context.Context, input map[string]any) (any, error) {
items := input["items"].([]interface{})
minLength := 5
if ml, ok := input["min_length"].(string); ok {
fmt.Sscanf(ml, "%d", &minLength)
}
var filtered []string
for _, item := range items {
str := fmt.Sprintf("%v", item)
if len(str) >= minLength {
filtered = append(filtered, str)
}
}
return filtered, nil
},
agent.WithCLI(),
agent.WithDefaultCLI(),
)
if err := a.Run(context.Background()); err != nil {
log.Fatal(err)
}
}Usage in pipeline:
# data.json
{
"items": ["a", "hello", "world", "go", "agent"]
}
# Filter and process
cat data.json | ./filter --set min_length=4 | jq '.[]'Example 8: Graceful Degradation
Agent that adapts to control plane availability:
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/Agent-Field/agentfield/sdk/go/agent"
)
func main() {
a, err := agent.New(agent.Config{
NodeID: "smart-agent",
AgentFieldURL: os.Getenv("AGENTFIELD_URL"),
CLIConfig: &agent.CLIConfig{
AppName: "smart-agent",
AppDescription: "Adaptive agent (works with or without control plane)",
},
})
if err != nil {
log.Fatal(err)
}
hasControlPlane := a.Config.AgentFieldURL != ""
a.RegisterReasoner("process", func(ctx context.Context, input map[string]any) (any, error) {
data := input["data"].(string)
if hasControlPlane {
// Try to use control plane features
res, err := a.Call(ctx, "nlp-agent.analyze", map[string]any{
"text": data,
})
if err == nil {
return res, nil
}
// Fallback to local if control plane call fails
log.Printf("Control plane call failed, using local processing: %v", err)
}
// Local processing
return map[string]any{
"processed": fmt.Sprintf("Locally processed: %s", data),
"mode": "standalone",
}, nil
},
agent.WithCLI(),
agent.WithDefaultCLI(),
)
if err := a.Run(context.Background()); err != nil {
log.Fatal(err)
}
}Usage:
# Standalone
./smart-agent --set data="hello"
# With control plane
AGENTFIELD_URL=http://localhost:8080 ./smart-agent --set data="hello"Building and Distribution
Local Build
go build -o my-agent
./my-agent --helpMulti-Platform Build
# Linux
GOOS=linux GOARCH=amd64 go build -o my-agent-linux-amd64
# macOS (Intel)
GOOS=darwin GOARCH=amd64 go build -o my-agent-darwin-amd64
# macOS (Apple Silicon)
GOOS=darwin GOARCH=arm64 go build -o my-agent-darwin-arm64
# Windows
GOOS=windows GOARCH=amd64 go build -o my-agent-windows-amd64.exeWith GoReleaser
builds:
- id: my-agent
binary: my-agent
main: ./main.go
goos:
- linux
- darwin
- windows
goarch:
- amd64
- arm64
archives:
- format: tar.gz
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"goreleaser release --snapshot --clean