This document covers Kong's hook interfaces that allow you to execute custom logic at various stages of the parsing lifecycle.
Called before values are reset to their defaults.
// BeforeReset is a hook interface called before values are reset to defaults
type BeforeReset interface {
BeforeReset(ctx *Context) error
}Called before resolvers are applied.
// BeforeResolve is a hook interface called before resolvers are applied
type BeforeResolve interface {
BeforeResolve(ctx *Context) error
}Called before values are applied to the target.
// BeforeApply is a hook interface called before values are applied to the target
type BeforeApply interface {
BeforeApply(ctx *Context) error
}Called after values are applied and validated.
// AfterApply is a hook interface called after values are applied and validated
type AfterApply interface {
AfterApply(ctx *Context) error
}Called after Run() is called.
// AfterRun is a hook interface called after Run() is called
type AfterRun interface {
AfterRun(ctx *Context) error
}Can be implemented by flags that want to be applied before any default commands.
// IgnoreDefault can be implemented by flags that want to be applied
// before any default commands
type IgnoreDefault interface {
IgnoreDefault()
}// WithBeforeReset registers a hook to run before field values are
// reset to their defaults
func WithBeforeReset(fn any) Option
// WithBeforeResolve registers a hook to run before resolvers are applied
func WithBeforeResolve(fn any) Option
// WithBeforeApply registers a hook to run before command line arguments
// are applied
func WithBeforeApply(fn any) Option
// WithAfterApply registers a hook to run after values are applied
// and validated
func WithAfterApply(fn any) Optionpackage main
import (
"fmt"
"github.com/alecthomas/kong"
)
type CLI struct {
Debug bool `help:"Enable debug mode."`
Serve struct {
Port int `help:"Port to listen on." default:"8080"`
} `cmd:"" help:"Start the server."`
}
// Implement BeforeReset on the CLI struct
func (c *CLI) BeforeReset(ctx *kong.Context) error {
fmt.Println("Resetting configuration to defaults...")
return nil
}
func main() {
var cli CLI
ctx := kong.Parse(&cli)
// BeforeReset will be called automatically during parsing
err := ctx.Run()
ctx.FatalIfErrorf(err)
}type CLI struct {
ConfigFile string `type:"path" help:"Configuration file."`
Host string `help:"Server host."`
Port int `help:"Server port."`
}
func (c *CLI) BeforeResolve(ctx *kong.Context) error {
fmt.Println("About to resolve configuration...")
// Load custom resolver if config file is specified
if c.ConfigFile != "" {
file, err := os.Open(c.ConfigFile)
if err != nil {
return err
}
defer file.Close()
resolver, err := kong.JSON(file)
if err != nil {
return err
}
ctx.AddResolver(resolver)
}
return nil
}
func main() {
var cli CLI
ctx := kong.Parse(&cli)
err := ctx.Run()
ctx.FatalIfErrorf(err)
}type CLI struct {
Verbose bool `help:"Enable verbose output."`
Output string `help:"Output file." type:"path"`
}
func (c *CLI) BeforeApply(ctx *kong.Context) error {
fmt.Println("About to apply command-line arguments...")
// Validate combinations before applying
if c.Verbose && c.Output != "" {
return fmt.Errorf("verbose mode not supported with output file")
}
return nil
}
func main() {
var cli CLI
ctx := kong.Parse(&cli)
err := ctx.Run()
ctx.FatalIfErrorf(err)
}type CLI struct {
Debug bool `help:"Enable debug mode."`
Serve struct {
Port int `help:"Port to listen on." default:"8080"`
Host string `help:"Host to bind to." default:"localhost"`
} `cmd:"" help:"Start the server."`
}
func (c *CLI) AfterApply(ctx *kong.Context) error {
fmt.Println("Configuration applied successfully")
// Perform post-application validation
if c.Debug {
fmt.Printf("Debug mode enabled\n")
fmt.Printf("Command: %s\n", ctx.Command())
}
return nil
}
func (s *Serve) AfterApply(ctx *kong.Context) error {
// Command-specific validation
if s.Port < 1024 && os.Geteuid() != 0 {
return fmt.Errorf("privileged ports require root access")
}
return nil
}
func main() {
var cli CLI
ctx := kong.Parse(&cli)
// AfterApply hooks will be called automatically
err := ctx.Run()
ctx.FatalIfErrorf(err)
}type CLI struct {
Serve struct {
Port int `help:"Port to listen on." default:"8080"`
} `cmd:"" help:"Start the server."`
}
func (s *Serve) Run() error {
fmt.Printf("Server started on port %d\n", s.Port)
// Server logic here
return nil
}
func (s *Serve) AfterRun(ctx *kong.Context) error {
fmt.Println("Server stopped, cleaning up...")
// Cleanup logic here
return nil
}
func main() {
var cli CLI
ctx := kong.Parse(&cli)
err := ctx.Run()
ctx.FatalIfErrorf(err)
// AfterRun will be called after Run() completes
}func beforeResetHook(ctx *kong.Context) error {
fmt.Println("Global before reset hook")
return nil
}
func afterApplyHook(ctx *kong.Context) error {
fmt.Println("Global after apply hook")
return nil
}
func main() {
var cli CLI
parser := kong.Must(&cli,
kong.WithBeforeReset(beforeResetHook),
kong.WithAfterApply(afterApplyHook),
)
ctx, err := parser.Parse(os.Args[1:])
parser.FatalIfErrorf(err)
err = ctx.Run()
ctx.FatalIfErrorf(err)
}type Logger interface {
Log(msg string)
}
type ConsoleLogger struct{}
func (c *ConsoleLogger) Log(msg string) {
fmt.Println(msg)
}
func beforeApplyHook(ctx *kong.Context, logger Logger) error {
logger.Log("Before apply hook with injected logger")
return nil
}
func afterApplyHook(ctx *kong.Context, logger Logger) error {
logger.Log("After apply hook with injected logger")
return nil
}
func main() {
var cli CLI
logger := &ConsoleLogger{}
parser := kong.Must(&cli,
kong.Bind(logger),
kong.WithBeforeApply(beforeApplyHook),
kong.WithAfterApply(afterApplyHook),
)
ctx, err := parser.Parse(os.Args[1:])
parser.FatalIfErrorf(err)
err = ctx.Run()
ctx.FatalIfErrorf(err)
}type CLI struct {
Serve struct{} `cmd:""`
}
func (c *CLI) BeforeReset(ctx *kong.Context) error {
fmt.Println("1. CLI.BeforeReset")
return nil
}
func (c *CLI) BeforeResolve(ctx *kong.Context) error {
fmt.Println("2. CLI.BeforeResolve")
return nil
}
func (c *CLI) BeforeApply(ctx *kong.Context) error {
fmt.Println("3. CLI.BeforeApply")
return nil
}
func (c *CLI) AfterApply(ctx *kong.Context) error {
fmt.Println("4. CLI.AfterApply")
return nil
}
func (s *Serve) Run() error {
fmt.Println("5. Serve.Run")
return nil
}
func (s *Serve) AfterRun(ctx *kong.Context) error {
fmt.Println("6. Serve.AfterRun")
return nil
}
// Execution order:
// 1. BeforeReset hooks
// 2. BeforeResolve hooks
// 3. BeforeApply hooks
// 4. AfterApply hooks
// 5. Run() method
// 6. AfterRun hookstype CLI struct {
ConfigFile string `type:"path" help:"Configuration file."`
}
func (c *CLI) BeforeResolve(ctx *kong.Context) error {
if c.ConfigFile == "" {
return fmt.Errorf("configuration file is required")
}
if _, err := os.Stat(c.ConfigFile); os.IsNotExist(err) {
return fmt.Errorf("configuration file not found: %s", c.ConfigFile)
}
return nil
}
func main() {
var cli CLI
ctx := kong.Parse(&cli)
// If BeforeResolve returns an error, parsing will fail
err := ctx.Run()
ctx.FatalIfErrorf(err)
}type CLI struct {
Debug bool `help:"Enable debug mode."`
Serve struct {
Port int `help:"Port to listen on." default:"8080"`
} `cmd:"" help:"Start the server."`
Build struct {
Output string `help:"Output directory."`
} `cmd:"" help:"Build the project."`
}
func (c *CLI) AfterApply(ctx *kong.Context) error {
command := ctx.Command()
switch command {
case "serve":
fmt.Println("Preparing to start server...")
// Server-specific setup
case "build":
fmt.Println("Preparing to build project...")
// Build-specific setup
}
return nil
}
func main() {
var cli CLI
ctx := kong.Parse(&cli)
err := ctx.Run()
ctx.FatalIfErrorf(err)
}type CLI struct {
Verbose bool `help:"Enable verbose output."`
Serve struct {
Port int `help:"Port to listen on." default:"8080"`
} `cmd:"" help:"Start the server."`
}
func (c *CLI) AfterApply(ctx *kong.Context) error {
// Access context information
fmt.Printf("Application: %s\n", ctx.Model.Name)
fmt.Printf("Command: %s\n", ctx.Command())
fmt.Printf("Args: %v\n", ctx.Args)
// Examine flags
flags := ctx.Flags()
fmt.Printf("Available flags: %d\n", len(flags))
// Check selected command
if selected := ctx.Selected(); selected != nil {
fmt.Printf("Selected: %s\n", selected.Name)
}
return nil
}
func main() {
var cli CLI
ctx := kong.Parse(&cli)
err := ctx.Run()
ctx.FatalIfErrorf(err)
}// HelpFlag that ignores default commands
type CustomHelpFlag bool
func (h CustomHelpFlag) IgnoreDefault() {
// Marker method - no implementation needed
}
func (h CustomHelpFlag) BeforeApply(ctx *kong.Context) error {
// Print help and exit
ctx.PrintUsage(false)
os.Exit(0)
return nil
}
type CLI struct {
Help CustomHelpFlag `short:"h" help:"Show help."`
Serve struct {
Port int `help:"Port to listen on." default:"8080"`
} `cmd:"" default:"1" help:"Start the server."`
}
func main() {
var cli CLI
ctx := kong.Parse(&cli)
// --help will be processed before the default "serve" command
err := ctx.Run()
ctx.FatalIfErrorf(err)
}type CLI struct {
startTime time.Time
Serve struct{} `cmd:""`
}
func (c *CLI) BeforeApply(ctx *kong.Context) error {
// Store state in the CLI struct
c.startTime = time.Now()
fmt.Println("Starting execution...")
return nil
}
func (c *CLI) AfterRun(ctx *kong.Context) error {
// Use stored state
duration := time.Since(c.startTime)
fmt.Printf("Execution completed in %v\n", duration)
return nil
}
func (s *Serve) Run(cli *CLI) error {
fmt.Println("Running serve command...")
// Access parent CLI state
fmt.Printf("Started at: %v\n", cli.startTime)
return nil
}
func main() {
var cli CLI
ctx := kong.Parse(&cli,
kong.Bind(&cli),
)
err := ctx.Run()
ctx.FatalIfErrorf(err)
}type CLI struct {
Verbose bool `help:"Enable verbose output."`
Serve struct {
Port int `help:"Port to listen on." default:"8080"`
} `cmd:"" help:"Start the server."`
}
func (c *CLI) BeforeReset(ctx *kong.Context) error {
if c.Verbose {
fmt.Println("[1] BeforeReset: Resetting to defaults")
}
return nil
}
func (c *CLI) BeforeResolve(ctx *kong.Context) error {
if c.Verbose {
fmt.Println("[2] BeforeResolve: About to resolve from external sources")
}
return nil
}
func (c *CLI) BeforeApply(ctx *kong.Context) error {
if c.Verbose {
fmt.Println("[3] BeforeApply: About to apply parsed values")
}
return nil
}
func (c *CLI) AfterApply(ctx *kong.Context) error {
if c.Verbose {
fmt.Println("[4] AfterApply: Values applied successfully")
}
return nil
}
func (s *Serve) Run(cli *CLI) error {
if cli.Verbose {
fmt.Println("[5] Run: Executing command")
}
fmt.Printf("Starting server on port %d\n", s.Port)
return nil
}
func (s *Serve) AfterRun(ctx *kong.Context) error {
cli := ctx.Model.Target.Interface().(*CLI)
if cli.Verbose {
fmt.Println("[6] AfterRun: Command completed")
}
return nil
}
func main() {
var cli CLI
ctx := kong.Parse(&cli,
kong.Bind(&cli),
)
err := ctx.Run()
ctx.FatalIfErrorf(err)
}