
Orpheus is a high-performance CLI framework designed to be super simple and ~30x faster than popular alternatives with zero third-party dependencies. Built on FlashFlags & go-errors, Orpheus provides a simple interface to create modern, secure, fast CLI apps similar to git.
See Orpheus in action - building a Git-like CLI with subcommands in minutes:
Click to view interactive demo
Features • Performance • Security • Quick Start • Storage System • Observability • Examples • API Reference • Philosophy
golang.org/x)Run is signal-aware by default; RunContext supports custom cancellation, deadlines, and tracingPrompter interface for user input (text, secrets, menus, confirmations) with terminal-safe maskingOrpheus is designed for Go 1.25.9+ environments and follows Long-Term Support guidelines to ensure consistent performance across production deployments.
Benchmark results comparing CLI framework performance:
AMD Ryzen 5 7520U with Radeon Graphics
BenchmarkOrpheus-8 1908495 634.5 ns/op 96 B/op 3 allocs/op
BenchmarkCobra-8 66 18439562 ns/op 3145 B/op 33 allocs/op
BenchmarkUrfaveCli-8 40767 30097 ns/op 8549 B/op 318 allocs/op
BenchmarkKingpin-8 293697 4294 ns/op 1988 B/op 40 allocs/op
BenchmarkStdFlag-8 1027216 1039 ns/op 945 B/op 13 allocs/op
Scenario: Command parsing with 3 flags (string, bool, string) and handler execution.
Reproduce benchmarks:
cd benchmarks/
go test -bench=. -benchmem
Complete Performance Benchmarks →
Orpheus implements defense-in-depth security with comprehensive validation against CLI attack vectors.
Protected Vectors: - Path traversal (case-insensitive, URL encoding, Windows device names) - Command/SQL/Script injection prevention - Control character and null byte filtering - Cross-platform consistency (Windows, Linux, macOS)
Run Security Tests:
make security # Run security test suite
make fuzz # Quick fuzz testing (30s)
make fuzz-long # Extended fuzzing (5min)
go get github.com/agilira/orpheus@v1.4.0 # Latest stable release
# or simply
go get github.com/agilira/orpheus # Always latest
package main
import (
"fmt"
"log"
"os"
"github.com/agilira/orpheus"
)
func main() {
app := orpheus.New("myapp").
SetDescription("My awesome CLI application").
SetVersion("1.0.0")
// Add commands with fluent interface
app.Command("start", "Start the service", func(ctx *orpheus.Context) error {
fmt.Println("Service starting...")
return nil
})
app.Command("stop", "Stop the service", func(ctx *orpheus.Context) error {
fmt.Println("Service stopping...")
return nil
})
// Run the application
if err := app.Run(os.Args[1:]); err != nil {
log.Fatal(err)
}
}
Run installs Orpheus' default signal-aware context, so command handlers can
observe Ctrl-C/SIGTERM cancellation through ctx.Context() without extra setup:
app.Command("sync", "Synchronize data", func(ctx *orpheus.Context) error {
return syncData(ctx.Context())
})
if err := app.Run(os.Args[1:]); err != nil {
log.Fatal(err)
}
Use RunContext when your application owns the lifecycle, needs a deadline, or
wants to provide a parent tracing context:
runCtx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
app.Command("sync", "Synchronize data", func(ctx *orpheus.Context) error {
return syncData(ctx.Context())
})
if err := app.RunContext(runCtx, os.Args[1:]); err != nil {
log.Fatal(err)
}
Generate shell completion scripts for your CLI:
# Bash completion (add to ~/.bashrc)
./myapp completion bash > /etc/bash_completion.d/myapp
# Zsh completion (add to ~/.zshrc)
./myapp completion zsh > /usr/local/share/zsh/site-functions/_myapp
# Fish completion
./myapp completion fish > ~/.config/fish/completions/myapp.fish
// Create a command with subcommands
remoteCmd := orpheus.NewCommand("remote", "Manage remote repositories")
// Add subcommands using fluent API (v1.0.7+ - now works correctly)
remoteCmd.Subcommand("add", "Add a remote", func(ctx *orpheus.Context) error {
name, url := ctx.GetArg(0), ctx.GetArg(1)
fmt.Printf("Added remote: %s -> %s\n", name, url)
return nil
}).AddFlag("--force", "Force add remote")
remoteCmd.Subcommand("list", "List remotes", func(ctx *orpheus.Context) error {
fmt.Println("origin\thttps://github.com/user/repo.git")
return nil
}).AddFlag("--verbose", "Show detailed information")
app.AddCommand(remoteCmd)
// Usage: ./myapp remote add --force origin https://github.com/user/repo.git
// ./myapp remote list --verbose
Note on subcommands vs positional arguments: if a parent command accepts both subcommands and free positional arguments (like
git stash [save]vsgit stash pop), the first non-flag argument is always matched as a subcommand. Design commands so that subcommand names do not collide with expected positional values. If a command needs both, use explicit subcommands for all variants (e.g.,stash save,stash pop,stash show).
Orpheus provides a pluggable storage system that allows CLI applications to persist state using various backends through a unified interface:
import (
"log"
"os"
"github.com/agilira/orpheus/pkg/orpheus"
)
func main() {
app := orpheus.New("myapp").
SetDescription("CLI app with persistent storage").
SetVersion("1.0.0")
// Configure storage (supports SQLite, Redis, File, and custom providers)
config := &orpheus.StorageConfig{
Provider: "sqlite",
Config: map[string]interface{}{
"path": "./myapp.db",
},
EnableMetrics: true,
}
app.ConfigureStorage(config)
// Commands can now use persistent storage
app.Command("set", "Store a key-value pair", setCommand)
app.Command("get", "Retrieve a value", getCommand)
if err := app.Run(os.Args[1:]); err != nil {
log.Fatal(err)
}
}
func setCommand(ctx *orpheus.Context) error {
storage := ctx.Storage()
if storage == nil {
return orpheus.ErrStorageNotConfigured
}
key := ctx.GetArg(0)
value := ctx.GetArg(1)
return storage.Set(ctx, key, []byte(value))
}
func getCommand(ctx *orpheus.Context) error {
storage, err := ctx.RequireStorage()
if err != nil {
return err
}
key := ctx.GetArg(0)
value, err := storage.Get(ctx, key)
if err != nil {
return err
}
fmt.Printf("Value: %s\n", string(value))
return nil
}
Key Features:
- Plugin Architecture: Dynamic .so loading for storage providers
- Zero Dependencies: No external storage libraries required
- Security Hardened: Input validation and plugin security checks
- Production Ready: Metrics, tracing, and audit logging integration
- Provider Agnostic: Unified interface for SQLite, Redis, File, and custom backends
Complete Storage Documentation →
Orpheus includes a built-in Prompter interface for interactive CLI workflows -- setup wizards, first-run configuration, guided input. Secrets are masked at the terminal level via golang.org/x/term.
app := orpheus.New("myapp").
SetDescription("Interactive CLI").
SetVersion("1.0.0").
SetPrompter(orpheus.NewTerminalPrompter())
app.Command("setup", "Run first-time setup", func(ctx *orpheus.Context) error {
p, err := ctx.RequirePrompter()
if err != nil {
return err
}
name, err := p.Ask("Your name:", "")
if err != nil {
return err
}
apiKey, err := p.AskSecret("API key:")
if err != nil {
return err
}
provider, err := p.Choose("LLM provider:", []string{"OpenAI", "Anthropic", "Ollama"})
if err != nil {
return err
}
confirm, err := p.Confirm("Save configuration?", true)
if err != nil {
return err
}
fmt.Printf("Name: %s, Provider: %d, Confirmed: %v\n", name, provider, confirm)
_ = apiKey // use securely
return nil
})
Prompter methods:
| Method | Purpose | Return |
|---|---|---|
Ask(prompt, default) |
Free-text input with optional default | (string, error) |
AskSecret(prompt) |
Masked password/key entry | (string, error) |
Choose(prompt, options) |
Numbered menu selection | (int, error) (0-based) |
Confirm(prompt, defaultYes) |
Yes/no question | (bool, error) |
The Prompter interface is testable by design: inject a custom implementation for unit tests without touching a real terminal.
Zero-overhead observability interfaces for production CLI applications:
import "context"
// Configure observability (all interfaces are optional)
app := orpheus.New("myapp").
SetLogger(myLogger). // Structured logging
SetAuditLogger(myAuditLogger). // Compliance and security
SetTracer(myTracer). // Distributed tracing
SetMetricsCollector(myMetrics) // Performance metrics
app.Command("deploy", "Deploy application", func(ctx *orpheus.Context) error {
// Structured logging
if logger := ctx.Logger(); logger != nil {
logger.Info(context.Background(), "Deployment started",
orpheus.StringField("environment", "production"),
orpheus.StringField("version", "v1.2.3"),
)
}
// Audit trail
if audit := ctx.AuditLogger(); audit != nil {
audit.LogCommand(context.Background(), "deploy", ctx.Args(), "demo-user")
audit.LogAccess(context.Background(), "production", "deploy", true)
}
// Distributed tracing
if tracer := ctx.Tracer(); tracer != nil {
spanCtx, span := tracer.StartSpan(context.Background(), "deploy_operation")
defer span.End()
// ... use spanCtx for downstream operations
}
// Performance metrics
if metrics := ctx.MetricsCollector(); metrics != nil {
counter := metrics.Counter("deployments_total", "Total deployments", "env")
counter.Inc(context.Background(), "production")
}
fmt.Println("Deployment completed")
return nil
})
Performance: Zero overhead when not configured (0.24 ns/op), minimal overhead when enabled (~24 ns/op)
Complete Observability Guide →
Orpheus's lyre was no ordinary instrument. It could make rivers pause mid-flow, convince stones to dance, and move even Hades and Persephone to tears. When the great musician played, the impossible became inevitable—not through force, but through the pure beauty of perfect harmony.
Yet Orpheu
$ claude mcp add orpheus \
-- python -m otcore.mcp_server <graph>