Repository: go-go-golems/glazed
Pull request #409 (2024-03-20 19:28:43)

:sparkles: Allow for wildcards when capturing parameters with glazed.parameter
- :sparkles: Properly parse defaults and add logs to cliopatra parameters
- :sparkles: Add ToInterfaceValue casting helper
- :art: Small updates to create-command and help system
- :sparkles: Update base functionality for QOL and new codegen
- :sparkles: Add wildcard glazed.parameter
- :sparkles: Support settings defaults with wildcard parameters
- :art: Rename InitializeStruct to Initialize StructWithLayer
- :art: Undo unnecessary annoying rename



Patch:
diff --git a/pkg/cli/cliopatra/capture.go b/pkg/cli/cliopatra/capture.go
index 3aec3b7..7f64846 100644
--- a/pkg/cli/cliopatra/capture.go
+++ b/pkg/cli/cliopatra/capture.go
@@ -33,6 +33,7 @@ func getCliopatraParameters(
 			Short: p.Help,
 			Type:  p.Type,
 			Value: v.Value,
+			Log:   v.Log,
 		}
 		if p.Type == parameters.ParameterTypeBool {
 			param.NoValue = true
@@ -44,12 +45,14 @@ func getCliopatraParameters(
 
 		param.IsArgument = p.IsArgument
 
-		// TODO(manuel, 2023-03-21) This would be easier if we knew why and from where something is set
-		//
-		// Right now we can only kind of guess, by doing some comparison.
-		//
-		// See https://github.com/go-go-golems/glazed/issues/239
-		if !p.IsEqualToDefault(v.Value) {
+		isFromDefault := true
+		for _, l := range v.Log {
+			if l.Source != parameters.SourceDefaults {
+				isFromDefault = false
+				break
+			}
+		}
+		if !isFromDefault || !p.IsEqualToDefault(v.Value) {
 			ret = append(ret, param)
 		}
 	})
@@ -60,7 +63,7 @@ func getCliopatraParameters(
 // NewProgramFromCapture is a helper function to help capture a cliopatra Program from
 // the description and the parsed layers a glazed command.
 //
-// It will go over all the ParameterDefinition (from the layers, which now also include the default layers).
+// It will go over all the ParameterDefinition (from all layers, which now also include the default layers).
 // and will try to create the best cliopatra map it can. It tries to resolve the prefixes
 // of layered parameters.
 //
@@ -77,10 +80,11 @@ func NewProgramFromCapture(
 		Description: description.Short,
 	}
 
-	// NOTE(manuel, 2023-03-21) Maybe we should add layers to the program capture too, to expose all the parameters
-	//
-	// See https://github.com/go-go-golems/cliopatra/issues/6
 	description.Layers.ForEach(func(_ string, layer layers.ParameterLayer) {
+		if layer.GetSlug() == "glazed-command" {
+			// skip the meta glazed command flags, which also contain the create-cliopatra flag
+			return
+		}
 		parsedLayer, ok := parsedLayers.Get(layer.GetSlug())
 		if !ok {
 			return
diff --git a/pkg/cli/cliopatra/program.go b/pkg/cli/cliopatra/program.go
index ee3e7b4..e038195 100644
--- a/pkg/cli/cliopatra/program.go
+++ b/pkg/cli/cliopatra/program.go
@@ -30,6 +30,7 @@ type Parameter struct {
 	Raw        string                   `yaml:"raw,omitempty"`
 	NoValue    bool                     `yaml:"noValue,omitempty"`
 	IsArgument bool                     `yaml:"isArgument,omitempty"`
+	Log        []parameters.ParseStep   `yaml:"log,omitempty"`
 }
 
 // NOTE(manuel, 2023-03-16) What about sandboxing the execution of the command, especially if it outputs files
@@ -37,15 +38,8 @@ type Parameter struct {
 // NOTE(manuel, 2023-03-16) What about measuring profiling regression
 
 func (p *Parameter) Clone() *Parameter {
-	return &Parameter{
-		Name:    p.Name,
-		Flag:    p.Flag,
-		Short:   p.Short,
-		Type:    p.Type,
-		Value:   p.Value,
-		Raw:     p.Raw,
-		NoValue: p.NoValue,
-	}
+	p_ := *p
+	return &p_
 }
 
 // Program describes a program to be executed by cliopatra.
@@ -80,6 +74,8 @@ type Program struct {
 	ExpectedFiles      map[string]string `yaml:"expectedFiles,omitempty"`
 }
 
+//var _ glazedcmds.WriterCommand = (*Program)(nil)
+
 type ProgramOption func(*Program)
 
 func WithName(name string) ProgramOption {
@@ -319,7 +315,8 @@ func (p *Program) AddRawFlag(raw ...string) {
 func (p *Program) RunIntoWriter(
 	ctx context.Context,
 	parsedLayers *layers.ParsedLayers,
-	w io.Writer) error {
+	w io.Writer,
+) error {
 	var err error
 	path := p.Path
 	if path == "" {
@@ -386,12 +383,12 @@ func (p *Program) ComputeArgs(ps *parameters.ParsedParameters) ([]string, error)
 			continue
 		}
 
-		value, ok := ps.Get(flag.Name)
+		parsedParameter, ok := ps.Get(flag.Name)
 		value_ := ""
 		if !ok {
 			value_ = flag.Raw
 		} else {
-			value_, err = parameters.RenderValue(flag.Type, value.Value)
+			value_, err = parsedParameter.RenderValue()
 			if err != nil {
 				return nil, errors.Wrapf(err, "could not render flag %s", flag.Name)
 			}
diff --git a/pkg/cli/cobra-parser.go b/pkg/cli/cobra-parser.go
index cf3ba96..a91ee61 100644
--- a/pkg/cli/cobra-parser.go
+++ b/pkg/cli/cobra-parser.go
@@ -43,7 +43,7 @@ func CobraCommandDefaultMiddlewares(
 	}
 
 	middlewares_ = append(middlewares_,
-		cmd_middlewares.SetFromDefaults(parameters.WithParseStepSource("defaults")),
+		cmd_middlewares.SetFromDefaults(parameters.WithParseStepSource(parameters.SourceDefaults)),
 	)
 
 	return middlewares_, nil
diff --git a/pkg/cli/cobra.go b/pkg/cli/cobra.go
index 99b69b2..ae71cbd 100644
--- a/pkg/cli/cobra.go
+++ b/pkg/cli/cobra.go
@@ -56,7 +56,7 @@ func BuildCobraCommandFromCommandAndFunc(
 		parsedLayers, err := cobraParser.Parse(cmd, args)
 		// show help if there is an error
 		if err != nil {
-			fmt.Println(err)
+			_, _ = fmt.Fprintln(os.Stderr, err)
 			err := cmd.Help()
 			cobra.CheckErr(err)
 			os.Exit(1)
diff --git a/pkg/cmds/layers/layer.go b/pkg/cmds/layers/layer.go
index 6291037..c968f5d 100644
--- a/pkg/cmds/layers/layer.go
+++ b/pkg/cmds/layers/layer.go
@@ -146,3 +146,47 @@ func (pl *ParameterLayers) AddToCobraCommand(cmd *cobra.Command) error {
 		return nil
 	})
 }
+
+func (pl *ParameterLayers) InitializeFromDefaults(options ...parameters.ParseStepOption) (*ParsedLayers, error) {
+	ret := NewParsedLayers()
+	err := pl.UpdateWithDefaults(ret, options...)
+	if err != nil {
+		return nil, err
+	}
+
+	return ret, nil
+}
+
+func InitializeParameterLayerWithDefaults(
+	v ParameterLayer,
+	parsedLayer *ParsedLayer,
+	options ...parameters.ParseStepOption,
+) error {
+	pds := v.GetParameterDefinitions()
+
+	err := pds.ForEachE(func(pd *parameters.ParameterDefinition) error {
+		err := pd.CheckParameterDefaultValueValidity()
+		if err != nil {
+			return err
+		}
+		if pd.Default != nil {
+			parsedLayer.Parameters.SetAsDefault(pd.Name, pd, *pd.Default, options...)
+		}
+		return nil
+	})
+
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (pl *ParameterLayers) UpdateWithDefaults(parsedLayers *ParsedLayers, options ...parameters.ParseStepOption) error {
+	err := pl.ForEachE(func(_ string, v ParameterLayer) error {
+		parsedLayer := parsedLayers.GetOrCreate(v)
+		return InitializeParameterLayerWithDefaults(v, parsedLayer, options...)
+	})
+
+	return err
+}
diff --git a/pkg/cmds/layers/parsed-layer.go b/pkg/cmds/layers/parsed-layer.go
index 9186978..99e5653 100644
--- a/pkg/cmds/layers/parsed-layer.go
+++ b/pkg/cmds/layers/parsed-layer.go
@@ -115,6 +115,14 @@ func NewParsedLayers(options ...ParsedLayersOption) *ParsedLayers {
 	return ret
 }
 
+func (p *ParsedLayers) Clone() *ParsedLayers {
+	ret := NewParsedLayers()
+	p.ForEach(func(k string, v *ParsedLayer) {
+		ret.Set(k, v.Clone())
+	})
+	return ret
+}
+
 func (p *ParsedLayers) GetOrCreate(layer ParameterLayer) *ParsedLayer {
 	slug := layer.GetSlug()
 	v, ok := p.Get(slug)
diff --git a/pkg/cmds/middlewares/update.go b/pkg/cmds/middlewares/update.go
index be8bb77..9fca7a8 100644
--- a/pkg/cmds/middlewares/update.go
+++ b/pkg/cmds/middlewares/update.go
@@ -17,27 +17,7 @@ func SetFromDefaults(options ...parameters.ParseStepOption) Middleware {
 			if err != nil {
 				return err
 			}
-			err = layers_.ForEachE(func(key string, l layers.ParameterLayer) error {
-				pds := l.GetParameterDefinitions()
-				parsedLayer := parsedLayers.GetOrCreate(l)
-
-				err := pds.ForEachE(func(pd *parameters.ParameterDefinition) error {
-					err := pd.CheckParameterDefaultValueValidity()
-					if err != nil {
-						return err
-					}
-					if pd.Default != nil {
-						parsedLayer.Parameters.SetAsDefault(pd.Name, pd, *pd.Default, options...)
-					}
-					return nil
-				})
-
-				if err != nil {
-					return err
-				}
-
-				return nil
-			})
+			err = layers_.UpdateWithDefaults(parsedLayers, options...)
 			if err != nil {
 				return err
 			}
diff --git a/pkg/cmds/parameters/initialize-struct.go b/pkg/cmds/parameters/initialize-struct.go
index de3dff5..d0092f0 100644
--- a/pkg/cmds/parameters/initialize-struct.go
+++ b/pkg/cmds/parameters/initialize-struct.go
@@ -2,12 +2,55 @@ package parameters
 
 import (
 	"encoding/json"
-	reflect_helpers "github.com/go-go-golems/glazed/pkg/helpers/reflect"
+	"fmt"
+	reflect2 "github.com/go-go-golems/glazed/pkg/helpers/reflect"
 	"github.com/pkg/errors"
+	"path/filepath"
 	"reflect"
 	"strings"
 )
 
+type tagOptions struct {
+	Name       string
+	FromJson   bool
+	IsWildcard bool
+}
+
+// parsedTagOptions processes a structure tag string into a struct of type tagOptions.
+//
+// - Split tag on ,
+// - Retrieves tag name
+// - Checks if "from_json" is present
+// - Returns an error if the tag is empty or nil otherwise.
+//
+// Returns: parsed *tagOptions
+// Error: If the tag is empty.
+func parsedTagOptions(tag string) (*tagOptions, error) {
+	options := strings.Split(tag, ",")
+	if len(options) == 0 {
+		return nil, errors.Errorf("invalid empty glazed.parameter tag")
+	}
+	name := options[0]
+	options = options[1:]
+
+	// if the name contains a *, then we are using a glob wildcard
+	isWildcard := strings.Contains(name, "*")
+
+	fromJson := false
+	for _, o := range options {
+		if o == "from_json" {
+			fromJson = true
+			break
+		}
+	}
+
+	return &tagOptions{
+		Name:       name,
+		FromJson:   fromJson,
+		IsWildcard: isWildcard,
+	}, nil
+}
+
 // InitializeStruct initializes a struct from a ParsedParameters map.
 //
 // It iterates through the struct fields looking for those tagged with
@@ -17,6 +60,11 @@ import (
 // If the tag open `from_json` is appended to `glazed.parameter` and the parameter
 // value is a string, bytes, rawMessage or FileData, the value is parsed from json.
 //
+// If the tag contains a wildcard, the function will match parameter names against the
+// wildcard pattern and store the matches in a map in the destination field. The map can
+// be of any type, as long as it is a map of strings. The same logic as for normal fields
+// will be applied to the map entries.
+//
 // Struct fields that are pointers to other structs are handled recursively.
 //
 // Struct fields that are pointers will be dereferenced. If the pointer is nil, a new value
@@ -34,6 +82,7 @@ import (
 //		Mappings            *parameters.FileData `glazed.parameter:"mappings"`
 //		Aliases             *map[string]Alias    `glazed.parameter:"aliases,from_json"`
 //		WaitForActiveShards string               `glazed.parameter:"wait_for_active_shards"`
+//		ApiKeys             map[string]string    `glazed.parameter:"*_api_key"`
 //	}
 //
 // Corresponding ParameterDefinitions:
@@ -64,6 +113,16 @@ import (
 //		parameters.ParameterTypeString,
 //		parameters.WithHelp("Set the number of active shards to wait for before the operation returns."),
 //	),
+//	parameters.NewParameterDefinition(
+//		"openai_api_key",
+//		parameters.ParameterTypeString,
+//		parameters.WithHelp("OpenAI API key"),
+//	),
+//	parameters.NewParameterDefinition(
+//		"google_api_key",
+//		parameters.ParameterTypeString,
+//		parameters.WithHelp("Google API key"),
+//	),
 //
 // Returns an error if:
 // - s is not a pointer to a struct
@@ -73,8 +132,6 @@ func (p *ParsedParameters) InitializeStruct(s interface{}) error {
 	if s == nil {
 		return errors.Errorf("Can't initialize nil struct")
 	}
-
-	// check that s is indeed a pointer to a struct
 	of := reflect.TypeOf(s)
 	if of.Kind() != reflect.Ptr {
 		return errors.Errorf("s is not a pointer")
@@ -83,6 +140,7 @@ func (p *ParsedParameters) InitializeStruct(s interface{}) error {
 		return errors.Errorf("s is not a pointer to a struct")
 	}
 	st := of.Elem()
+	v := reflect.ValueOf(s).Elem()
 
 	for i := 0; i < st.NumField(); i++ {
 		field := st.Field(i)
@@ -95,101 +153,144 @@ func (p *ParsedParameters) InitializeStruct(s interface{}) error {
 			return errors.Wrapf(err, "failed to parse glazed.parameter tag for field %s", field.Name)
 		}
 
-		parameter, ok := p.Get(options.Name)
-		if !ok {
-			continue
+		if options.IsWildcard {
+			dst := v.FieldByName(field.Name)
+			if dst.Kind() != reflect.Map {
+				return errors.Errorf("wildcard parameters require a map field, field %s is not a map", field.Name)
+			}
+			if err := p.setWildcardValues(dst, options.Name, options.FromJson); err != nil {
+				return errors.Wrapf(err, "failed to set wildcard values for %s", options.Name)
+			}
+		} else {
+			parameter, ok := p.Get(options.Name)
+			if !ok {
+				continue
+			}
+			dst := v.FieldByName(field.Name)
+			if err := p.setTargetValue(dst, parameter.Value, options.FromJson); err != nil {
+				return errors.Wrapf(err, "failed to set value for %s", options.Name)
+			}
 		}
-		dst := reflect.ValueOf(s).Elem().FieldByName(field.Name)
-		kind := field.Type.Kind()
+	}
+	return nil
+}
 
-		if dst.Type() == reflect.TypeOf(parameter.Value) && !options.FromJson {
-			dst.Set(reflect.ValueOf(parameter.Value))
-			continue
-		}
+// setWildcardValues matches parameter names from ParsedParameters against a supplied pattern using the
+// filepath.Match method.
+//
+// The pattern may consist of literal characters,
+// character ranges enclosed in square brackets, and the wildcards * and ?.
+// The wildcard * matches zero or more characters, and the wildcard ? matches a single character.
+//
+// The function sets the matches in a map in the destination reflect.Value,
+// using the parameter name as the key and the parameter value as the map value.
+//
+// This way, you can match multiple flags at once and stored them in a map.
+//
+// Parameters:
+//   - dst: A reflect.Value acting as the container for storing the matched keys and their parameter values.
+//   - pattern: String pattern used for matching parameter names. It can include literal characters,
+//     character ranges enclosed in brackets and wildcards.
+func (p *ParsedParameters) setWildcardValues(dst reflect.Value, pattern string, fromJson bool) error {
+	// Get the type of elements in dst
+	var elemType reflect.Type
+	if dst.Kind() != reflect.Map {
+		return fmt.Errorf("destination is not a map")
+	}
+	elemType = dst.Type().Elem()
+	keyType := dst.Type().Key()
 
-		wasPointer := false
-		if kind == reflect.Ptr {
-			elem := field.Type.Elem()
-			kind = elem.Kind()
-			if dst.IsNil() {
-				kind = elem.Kind()
-				value := reflect.New(elem)
-				dst.Set(value)
-			}
-			dst = dst.Elem()
-			wasPointer = true
-		}
+	if dst.IsNil() {
+		dst.Set(reflect.MakeMapWithSize(reflect.MapOf(reflect.TypeOf(""), elemType), 0))
+	}
 
-		if options.FromJson {
-			if !wasPointer {
-				return errors.Errorf("from_json tag can only be used on pointer fields")
+	err := p.ForEachE(func(paramName string, parameter *ParsedParameter) error {
+		if matched, _ := filepath.Match(pattern, paramName); matched {
+			// Check if the type of parameter.Value is compatible with the map's value type
+			if reflect.TypeOf(parameter.Value) != elemType {
+				return fmt.Errorf("type mismatch: expected type %s, got %s", elemType, reflect.TypeOf(parameter.Value))
 			}
 
-			switch parameter.Value.(type) {
-			case *FileData:
-				err := json.Unmarshal([]byte(parameter.Value.(*FileData).Content), dst.Interface())
-				if err != nil {
-					return errors.Wrapf(err, "failed to unmarshal json for %s", options.Name)
-				}
-			case string:
-				err := json.Unmarshal([]byte(parameter.Value.(string)), dst.Interface())
-				if err != nil {
-					return errors.Wrapf(err, "failed to unmarshal json for %s", options.Name)
-				}
-			case []byte, json.RawMessage:
-				err := json.Unmarshal(parameter.Value.([]byte), dst.Interface())
-				if err != nil {
-					return errors.Wrapf(err, "failed to unmarshal json for %s", options.Name)
+			paramValue := reflect.ValueOf(parameter.Value)
+
+			// Check if the type of parameter.Value is directly assignable to the map's value type
+			if !paramValue.Type().AssignableTo(elemType) {
+				// Check if the type of parameter.Value can be converted to the map's value type
+				if paramValue.Type().ConvertibleTo(elemType) {
+					paramValue = paramValue.Convert(elemType)
+				} else {
+					return fmt.Errorf("type mismatch: cannot assign type %s to type %s", paramValue.Type(), elemType)
 				}
-			default:
-				return errors.Errorf("unsupported type for %s", options.Name)
 			}
 
-			continue
-		}
-
-		if kind == reflect.Struct {
-			err := p.InitializeStruct(dst.Interface())
-			if err != nil {
-				return errors.Wrapf(err, "failed to initialize struct for %s", options.Name)
+			// Set the value in the map
+			keyValue := reflect.ValueOf(paramName)
+			if !keyValue.Type().AssignableTo(keyType) {
+				if keyValue.Type().ConvertibleTo(keyType) {
+					keyValue = keyValue.Convert(keyType)
+				} else {
+					return fmt.Errorf("type mismatch: cannot assign type %s to type %s", keyValue.Type(), keyType)
+				}
 			}
-			continue
-		}
 
-		kind2 := dst.Kind()
-		_ = kind2
-		err = reflect_helpers.SetReflectValue(dst, parameter.Value)
-		if err != nil {
-			return errors.Wrapf(err, "failed to set value %v for %s from value %v", options.Name, dst, parameter.Value)
+			dst.SetMapIndex(keyValue, paramValue)
 		}
+		return nil
+	})
+
+	if err != nil {
+		return err
 	}
 
 	return nil
 }
 
-type tagOptions struct {
-	Name     string
-	FromJson bool
-}
-
-func parsedTagOptions(tag string) (*tagOptions, error) {
-	options := strings.Split(tag, ",")
-	if len(options) == 0 {
-		return nil, errors.Errorf("invalid empty glazed.parameter tag")
+func (p *ParsedParameters) setTargetValue(dst reflect.Value, value interface{}, fromJson bool) error {
+	// Handle pointer fields
+	wasPointer := false
+	if dst.Kind() == reflect.Ptr {
+		wasPointer = true
+		if dst.IsNil() {
+			newValue := reflect.New(dst.Type().Elem())
+			dst.Set(newValue)
+		}
+		dst = dst.Elem()
 	}
-	name := options[0]
-	options = options[1:]
 
-	fromJson := false
-	for _, o := range options {
-		if o == "from_json" {
-			fromJson = true
-			break
+	// Handle JSON unmarshalling
+	if fromJson {
+		if !wasPointer {
+			return errors.Errorf("from_json tag can only be used on pointer fields")
 		}
+		jsonData, ok := value.([]byte)
+		if !ok {
+			str, ok := value.(string)
+			if !ok {
+				return errors.Errorf("expected string or []byte for JSON unmarshalling, got %T", value)
+			}
+			jsonData = []byte(str)
+		}
+		if err := json.Unmarshal(jsonData, dst.Addr().Interface()); err != nil {
+			return errors.Wrapf(err, "failed to unmarshal JSON")
+		}
+		return nil
 	}
 
-	return &tagOptions{
-		Name:     name,
-		FromJson: fromJson,
-	}, nil
+	// Direct assignment if types are compatible
+	if dst.Type() == reflect.TypeOf(value) {
+		dst.Set(reflect.ValueOf(value))
+		return nil
+	}
+
+	// Recursive struct initialization
+	if dst.Kind() == reflect.Struct {
+		return p.InitializeStruct(dst.Addr().Interface())
+	}
+
+	err := reflect2.SetReflectValue(dst, value)
+	if err != nil {
+		return errors.Wrapf(err, "failed to set value for field")
+	}
+
+	return nil
 }
diff --git a/pkg/cmds/parameters/parameters.go b/pkg/cmds/parameters/parameters.go
index ad574f7..8ccbca7 100644
--- a/pkg/cmds/parameters/parameters.go
+++ b/pkg/cmds/parameters/parameters.go
@@ -2,6 +2,7 @@ package parameters
 
 import (
 	"fmt"
+	"path/filepath"
 	"reflect"
 	"time"
 
@@ -273,58 +274,27 @@ func (p *ParameterDefinition) SetValueFromInterface(value reflect.Value, v inter
 	return nil
 }
 
+// ParsedParametersFromDefaults uses the parameter definitions default values to create a ParsedParameters
+// object.
+func (pds *ParameterDefinitions) ParsedParametersFromDefaults() *ParsedParameters {
+	ret := NewParsedParameters()
+	pds.ForEach(func(definition *ParameterDefinition) {
+		ret.UpdateValue(definition.Name, definition, definition.Default,
+			WithParseStepSource("defaults"),
+			WithParseStepValue(definition.Default),
+		)
+	})
+	return ret
+}
+
 // InitializeStructFromDefaults initializes a struct from a map of parameter definitions.
 //
 // Each field in the struct annotated with tag `glazed.parameter` will be set to the default value of
 // the corresponding `ParameterDefinition`. If no `ParameterDefinition` is found for a field, an error
 // is returned.
 func (pds *ParameterDefinitions) InitializeStructFromDefaults(s interface{}) error {
-	// check that s is indeed a pointer to a struct
-	if reflect.TypeOf(s).Kind() != reflect.Ptr {
-		return errors.Errorf("s is not a pointer")
-	}
-	if reflect.TypeOf(s).Elem().Kind() != reflect.Struct {
-		return errors.Errorf("s is not a pointer to a struct")
-	}
-	st := reflect.TypeOf(s).Elem()
-
-	for i := 0; i < st.NumField(); i++ {
-		field := st.Field(i)
-		v, ok := field.Tag.Lookup("glazed.parameter")
-		if !ok {
-			continue
-		}
-		parameter, ok := pds.Get(v)
-		if !ok {
-			return errors.Errorf("unknown parameter %s", v)
-		}
-		value := reflect.ValueOf(s).Elem().FieldByName(field.Name)
-
-		if field.Type.Kind() == reflect.Ptr {
-			if value.IsNil() {
-				value.Set(reflect.New(field.Type.Elem()))
-			}
-			if field.Type.Elem().Kind() == reflect.Struct {
-				err := pds.InitializeStructFromDefaults(value.Interface())
-				if err != nil {
-					return errors.Wrapf(err, "failed to initialize struct for %s", v)
-				}
-			} else {
-				err := parameter.SetValueFromDefault(value.Elem())
-				if err != nil {
-					return errors.Wrapf(err, "failed to set value for %s", v)
-				}
-			}
-
-		}
-
-		err := parameter.SetValueFromDefault(value)
-		if err != nil {
-			return errors.Wrapf(err, "failed to set value for %s", v)
-		}
-	}
-
-	return nil
+	parsedParameters := pds.ParsedParametersFromDefaults()
+	return parsedParameters.InitializeStruct(s)
 }
 
 // InitializeDefaultsFromStruct initializes the parameters definitions from a struct.
@@ -350,19 +320,55 @@ func (pds *ParameterDefinitions) InitializeDefaultsFromStruct(
 
 	for i := 0; i < st.NumField(); i++ {
 		field := st.Field(i)
-		v, ok := field.Tag.Lookup("glazed.parameter")
+		tag, ok := field.Tag.Lookup("glazed.parameter")
 		if !ok {
 			continue
 		}
-		parameter, ok := pds.Get(v)
+
+		tagOptions, err := parsedTagOptions(tag)
+		if err != nil {
+			return err
+		}
+
+		if tagOptions.IsWildcard {
+			err = pds.ForEachE(func(pd *ParameterDefinition) error {
+				if matched, _ := filepath.Match(tagOptions.Name, pd.Name); matched {
+					mapValue := reflect.ValueOf(s).Elem().FieldByName(field.Name)
+
+					// check that mapValue is a map of strings
+					if mapValue.Kind() != reflect.Map {
+						return errors.Errorf("wildcard parameters require a map field, field %s is not a map", field.Name)
+					}
+					if mapValue.Type().Key().Kind() != reflect.String {
+						return errors.Errorf("wildcard parameters require a map of strings, field %s is not a map of strings", field.Name)
+					}
+
+					// look up pd.Name in the map and if present, set the default
+					value := mapValue.MapIndex(reflect.ValueOf(pd.Name))
+					if value.IsValid() {
+						err = pd.SetDefaultFromValue(value)
+						if err != nil {
+							return err
+						}
+					}
+				}
+				return nil
+			})
+			if err != nil {
+				return err
+			}
+			continue
+		}
+
+		parameter, ok := pds.Get(tagOptions.Name)
 		if !ok {
-			return errors.Errorf("unknown parameter %s", v)
+			return errors.Errorf("unknown parameter %s", tag)
 		}
 		value := reflect.ValueOf(s).Elem().FieldByName(field.Name)
 
-		err := parameter.SetDefaultFromValue(value)
+		err = parameter.SetDefaultFromValue(value)
 		if err != nil {
-			return errors.Wrapf(err, "failed to set default value for %s", v)
+			return errors.Wrapf(err, "failed to set default value for %s", tag)
 		}
 	}
 
@@ -660,6 +666,14 @@ func (pds *ParameterDefinitions) GetFlags() *ParameterDefinitions {
 	return ret
 }
 
+func (pds *ParameterDefinitions) ToList() []*ParameterDefinition {
+	ret := []*ParameterDefinition{}
+	for v := pds.Oldest(); v != nil; v = v.Next() {
+		ret = append(ret, v.Value)
+	}
+	return ret
+}
+
 func (pds *ParameterDefinitions) GetDefaultValue(key string, defaultValue interface{}) interface{} {
 	v, ok := pds.Get(key)
 	if !ok || v.Default == nil {
diff --git a/pkg/cmds/parameters/parse.go b/pkg/cmds/parameters/parse.go
index d3260b7..21ea7be 100644
--- a/pkg/cmds/parameters/parse.go
+++ b/pkg/cmds/parameters/parse.go
@@ -23,6 +23,8 @@ type ParseStep struct {
 	Metadata map[string]interface{}
 }
 
+const SourceDefaults = "defaults"
+
 // ParseParameter parses command line arguments according to the given ParameterDefinition.
 // It returns the parsed parameter value and a non-nil error if parsing failed.
 //
diff --git a/pkg/cmds/parameters/parsed-parameter.go b/pkg/cmds/parameters/parsed-parameter.go
index 20b27f5..a5c77d8 100644
--- a/pkg/cmds/parameters/parsed-parameter.go
+++ b/pkg/cmds/parameters/parsed-parameter.go
@@ -2,6 +2,7 @@ package parameters
 
 import (
 	"github.com/go-go-golems/glazed/pkg/helpers/cast"
+	"github.com/pkg/errors"
 	"github.com/wk8/go-ordered-map/v2"
 )
 
@@ -62,6 +63,10 @@ func (p *ParsedParameter) Update(value interface{}, options ...ParseStepOption)
 	p.Log = append(p.Log, step)
 }
 
+func (p *ParsedParameter) RenderValue() (string, error) {
+	return RenderValue(p.ParameterDefinition.Type, p.Value)
+}
+
 // UpdateWithLog sets the value of the parsedParameter, and appends the given log.
 func (p *ParsedParameter) UpdateWithLog(value interface{}, log ...ParseStep) {
 	p.Value = value
@@ -210,6 +215,19 @@ func (p *ParsedParameters) UpdateValue(
 	v_.Update(v, options...)
 }
 
+func (p *ParsedParameters) MustUpdateValue(
+	key string,
+	v interface{},
+	options ...ParseStepOption,
+) error {
+	v_, ok := p.Get(key)
+	if !ok {
+		return errors.Errorf("parameter %s not found", key)
+	}
+	v_.Update(v, options...)
+	return nil
+}
+
 func (p *ParsedParameters) UpdateWithLog(
 	key string, pd *ParameterDefinition,
 	v interface{},
@@ -234,12 +252,16 @@ func (p *ParsedParameters) SetAsDefault(
 	}
 }
 
+// ForEach applies the passed function to each key-value pair from oldest to newest
+// in ParsedParameters.
 func (p *ParsedParameters) ForEach(f func(key string, value *ParsedParameter)) {
 	for v := p.Oldest(); v != nil; v = v.Next() {
 		f(v.Key, v.Value)
 	}
 }
 
+// ForEachE applies the passed function (that returns an error) to each pair in
+// ParsedParameters. It stops at, and returns, the first error encountered.
 func (p *ParsedParameters) ForEachE(f func(key string, value *ParsedParameter) error) error {
 	for v := p.Oldest(); v != nil; v = v.Next() {
 		err := f(v.Key, v.Value)
@@ -269,6 +291,7 @@ func (p *ParsedParameters) MergeAsDefault(other *ParsedParameters, options ...Pa
 	return p
 }
 
+// ToMap converts ParsedParameters to map[string]interface{} by assigning each ParsedParameter's value to its key.
 func (p *ParsedParameters) ToMap() map[string]interface{} {
 	ret := map[string]interface{}{}
 	p.ForEach(func(k string, v *ParsedParameter) {
@@ -277,6 +300,8 @@ func (p *ParsedParameters) ToMap() map[string]interface{} {
 	return ret
 }
 
+// ToInterfaceMap converts ParsedParameters to map[string]interface{} by converting each ParsedParameter's value to interface{}.
+// It returns an error if it fails to convert any ParsedParameter's value.
 func (p *ParsedParameters) ToInterfaceMap() (map[string]interface{}, error) {
 	ret := map[string]interface{}{}
 	err := p.ForEachE(func(k string, v *ParsedParameter) error {
diff --git a/pkg/codegen/helpers.go b/pkg/codegen/helpers.go
index 8a599ea..67b51b8 100644
--- a/pkg/codegen/helpers.go
+++ b/pkg/codegen/helpers.go
@@ -1,9 +1,11 @@
 package codegen
 
 import (
+	"fmt"
 	"github.com/dave/jennifer/jen"
 	"github.com/pkg/errors"
 	"reflect"
+	"strconv"
 )
 
 func StructTypeToJen(typ reflect.Type) (jen.Code, error) {
@@ -259,3 +261,123 @@ func LiteralToJen(v reflect.Value) (jen.Code, error) {
 		return nil, errors.Errorf("unsupported type %s", v.Kind())
 	}
 }
+
+func ToGoCode(v interface{}) (string, error) {
+	return ValueToGoCode(reflect.ValueOf(v))
+}
+
+func ValueToGoCode(v reflect.Value) (string, error) {
+	switch v.Kind() {
+	case reflect.String:
+		return strconv.Quote(v.String()), nil
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		return fmt.Sprintf("%d", v.Int()), nil
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+		return fmt.Sprintf("%d", v.Uint()), nil
+	case reflect.Float32, reflect.Float64:
+		return fmt.Sprintf("%f", v.Float()), nil
+	case reflect.Bool:
+		return fmt.Sprintf("%t", v.Bool()), nil
+	case reflect.Slice, reflect.Array:
+		return SliceToGoCode(v)
+	case reflect.Struct:
+		return StructToGoCode(v)
+	case reflect.Map:
+		return MapToGoCode(v)
+	case reflect.Ptr:
+		if v.IsNil() {
+			return "nil", nil
+		}
+
+		if v.Elem().Kind() == reflect.Interface {
+			// We have a pointer to an interface, now let's see if it contains a string
+			actualValue := v.Elem().Elem() // This will dereference the interface to get the actual value
+			if actualValue.Kind() == reflect.String {
+				// We found a string inside the interface, return it quoted
+				return strconv.Quote(actualValue.String()), nil
+			} else {
+				// It's not a string, handle other types or return an error
+				return ValueToGoCode(actualValue)
+			}
+		}
+
+		code, err := ValueToGoCode(v.Elem())
+		if err != nil {
+			return "", err
+		}
+		// Use a temporary variable for the pointer's value
+		s := v.Elem().Type().String()
+		if s != "interface {}" {
+			s = "*" + s
+		}
+		return fmt.Sprintf("func() %s { tmp := %s; return &tmp }()", s, code), nil
+
+	case reflect.Interface:
+		if v.IsNil() {
+			return "nil", nil
+		}
+		return ValueToGoCode(v.Elem())
+	// Add more types as needed
+	case reflect.Invalid, reflect.Uintptr, reflect.Complex64, reflect.Complex128, reflect.Chan, reflect.Func, reflect.UnsafePointer:
+		return fmt.Sprintf("/* Unsupported type: %s */", v.Type().String()), errors.Errorf("unsupported type %s", v.Kind())
+	}
+
+	return "", errors.Errorf("unsupported type %s", v.Kind())
+}
+
+func SliceToGoCode(v reflect.Value) (string, error) {
+	result := "[]"
+	if v.Len() == 0 {
+		result += v.Type().Elem().String() + "{}"
+		return result, nil
+	}
+
+	result += v.Type().Elem().String() + "{"
+	for i := 0; i < v.Len(); i++ {
+		if i > 0 {
+			result += ", "
+		}
+		code, err := ValueToGoCode(v.Index(i))
+		if err != nil {
+			return "", err
+		}
+		result += code
+	}
+	result += "}"
+	return result, nil
+}
+
+func StructToGoCode(v reflect.Value) (string, error) {
+	result := "&" + v.Type().String() + "{"
+	for i := 0; i < v.NumField(); i++ {
+		if i > 0 {
+			result += ", "
+		}
+		field := v.Type().Field(i)
+		code, err := ValueToGoCode(v.Field(i))
+		if err != nil {
+			return "", err
+		}
+		result += field.Name + ": " + code
+	}
+	result += "}"
+	return result, nil
+}
+
+func MapToGoCode(v reflect.Value) (string, error) {
+	result := "map[" + v.Type().Key().String() + "]" + v.Type().Elem().String() + "{"
+	for _, key := range v.MapKeys() {
+		val := v.MapIndex(key)
+		code, err := ValueToGoCode(val)
+		if err != nil {
+			return "", err
+		}
+		keyCode, err := ValueToGoCode(key)
+		if err != nil {
+			return "", err
+		}
+		result += keyCode + ": " + code + ", "
+	}
+	result = result[:len(result)-2] + "}" // Remove the last ", "
+	return result, nil
+}
diff --git a/pkg/doc/doc.go b/pkg/doc/doc.go
index 0751757..1b18b74 100644
--- a/pkg/doc/doc.go
+++ b/pkg/doc/doc.go
@@ -5,7 +5,7 @@ import (
 	"github.com/go-go-golems/glazed/pkg/help"
 )
 
-//go:embed *
+//go:embed **/*.md
 var docFS embed.FS
 
 func AddDocToHelpSystem(helpSystem *help.HelpSystem) error {
diff --git a/pkg/help/cobra.go b/pkg/help/cobra.go
index 2a4e4eb..435da81 100644
--- a/pkg/help/cobra.go
+++ b/pkg/help/cobra.go
@@ -270,6 +270,10 @@ func NewCobraHelpCommand(hs *HelpSystem) *cobra.Command {
 
 			list, _ := c.Flags().GetBool("list")
 
+			if showAllSections || topics || examples || applications || tutorials {
+				list = true
+			}
+
 			options := &RenderOptions{
 				Query:           qb,
 				ShowAllSections: showAllSections,
@@ -300,6 +304,10 @@ func NewCobraHelpCommand(hs *HelpSystem) *cobra.Command {
 					_, _ = fmt.Fprint(c.OutOrStderr(), s)
 					return
 				}
+			} else {
+				// TODO(manuel, 2022-12-09): We could show a main help page if specified
+				cobra.CheckErr(renderCommandHelpPage(root, options, hs))
+				return
 			}
 
 			// if we couldn't find an explicit help page, show command help
@@ -314,7 +322,6 @@ func NewCobraHelpCommand(hs *HelpSystem) *cobra.Command {
 				}
 			} else {
 				cobra.CheckErr(renderCommandHelpPage(cmd, options, hs))
-
 			}
 		},
 	}
diff --git a/pkg/helpers/cast/interface.go b/pkg/helpers/cast/interface.go
new file mode 100644
index 0000000..aeade2a
--- /dev/null
+++ b/pkg/helpers/cast/interface.go
@@ -0,0 +1,73 @@
+package cast
+
+import (
+	"errors"
+	"reflect"
+)
+
+// ToInterfaceValue converts a given value of any basic Go type (scalars, slices, arrays, maps) into a corresponding
+// interface{}. Scalar types are returned as is. Slices and arrays are converted to []interface{} with each element
+// being recursively processed. Maps are converted to map[string]interface{} with each value being recursively
+// processed. The function uses reflection to handle various types dynamically. It returns an error if it encounters
+// a type that it cannot process, or if map keys are not strings.
+func ToInterfaceValue(value interface{}) (interface{}, error) {
+	if nil == value {
+		return nil, nil
+	}
+	val := reflect.ValueOf(value)
+	switch val.Kind() {
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
+		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
+		reflect.Float32, reflect.Float64,
+		reflect.Bool, reflect.String:
+		return val.Interface(), nil // Scalars are returned as-is.
+
+	case reflect.Slice, reflect.Array:
+		return processSlice(val)
+
+	case reflect.Map:
+		return processMap(val)
+
+	case reflect.Invalid, reflect.Complex64, reflect.Complex128, reflect.Chan, reflect.Func, reflect.Interface, reflect.Ptr, reflect.Struct, reflect.UnsafePointer:
+		return nil, errors.New("unsupported type")
+	}
+
+	return nil, errors.New("unsupported type")
+}
+
+// processSlice takes a reflect.Value representing a slice or an array and converts it into []interface{}.
+// Each element of the input slice/array is recursively converted to an interface{} using
+// toInterfaceValue. It returns a converted slice and any error encountered during the conversion.
+func processSlice(slice reflect.Value) ([]interface{}, error) {
+	result := make([]interface{}, slice.Len())
+	for i := 0; i < slice.Len(); i++ {
+		elem := slice.Index(i)
+		convertedElem, err := ToInterfaceValue(elem.Interface())
+		if err != nil {
+			return nil, err
+		}
+		result[i] = convertedElem
+	}
+	return result, nil
+}
+
+// processMap takes a reflect.Value representing a map and converts it into map[string]interface{} recursively.
+// The function returns an error if it encounters non-string keys or if any value is of an
+// unsupported type.
+func processMap(mapValue reflect.Value) (map[string]interface{}, error) {
+	result := make(map[string]interface{})
+	for _, key := range mapValue.MapKeys() {
+		val := mapValue.MapIndex(key)
+		convertedVal, err := ToInterfaceValue(val.Interface())
+		if err != nil {
+			return nil, err
+		}
+
+		// Ensure the key is a string
+		if key.Kind() != reflect.String {
+			return nil, errors.New("map key is not a string")
+		}
+		result[key.String()] = convertedVal
+	}
+	return result, nil
+}
diff --git a/pkg/helpers/reflect/reflect.go b/pkg/helpers/reflect/reflect.go
index b6af990..fcc7622 100644
--- a/pkg/helpers/reflect/reflect.go
+++ b/pkg/helpers/reflect/reflect.go
@@ -8,11 +8,12 @@ import (
 )
 
 func SetReflectValue(dst reflect.Value, src interface{}) error {
+	kind := dst.Kind()
 	if !dst.IsValid() {
-		return fmt.Errorf("cannot set invalid reflect.Value of type %s", dst.Kind())
+		return fmt.Errorf("cannot set invalid reflect.Value of type %s", kind)
 	}
 	if !dst.CanSet() {
-		return fmt.Errorf("cannot set reflect.Value of type %s", dst.Kind())
+		return fmt.Errorf("cannot set reflect.Value of type %s", kind)
 	}
 
 	if src == nil {
@@ -20,277 +21,336 @@ func SetReflectValue(dst reflect.Value, src interface{}) error {
 		return nil
 	}
 
-	//exhaustive:ignore
-	switch dst.Kind() {
-	case reflect.String:
-		if s, ok := src.(string); ok {
-			dst.SetString(s)
-			return nil
-		}
-		return fmt.Errorf("cannot set reflect.Value of type %s from %T", dst.Kind(), src)
+	setValue := func(src interface{}) error {
+		//exhaustive:ignore
+		switch kind {
+		case reflect.String:
+			if s, ok := src.(string); ok {
+				dst.SetString(s)
+				return nil
+			}
+			return fmt.Errorf("cannot set reflect.Value of type %s from %T", kind, src)
 
-	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
-		if s, ok := src.(string); ok {
-			i, err := strconv.ParseUint(s, 10, 64)
-			if err != nil {
-				return err
+		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+			if s, ok := src.(string); ok {
+				i, err := strconv.ParseUint(s, 10, 64)
+				if err != nil {
+					return err
+				}
+				dst.SetUint(i)
+				return nil
 			}
-			dst.SetUint(i)
-			return nil
-		}
-		if i, ok := src.(uint64); ok {
-			dst.SetUint(i)
-			return nil
-		}
-		if i, ok := src.(uint); ok {
-			dst.SetUint(uint64(i))
-			return nil
-		}
-		if i, ok := src.(uint8); ok {
-			dst.SetUint(uint64(i))
-			return nil
-		}
-		if i, ok := src.(uint16); ok {
-			dst.SetUint(uint64(i))
-			return nil
-		}
-		if i, ok := src.(uint32); ok {
-			dst.SetUint(uint64(i))
-			return nil
-		}
-		if i, ok := src.(int64); ok {
-			dst.SetUint(uint64(i))
-			return nil
-		}
-		if i, ok := src.(int); ok {
-			dst.SetUint(uint64(i))
-			return nil
-		}
-		if i, ok := src.(int8); ok {
-			dst.SetUint(uint64(i))
-			return nil
-		}
-		if i, ok := src.(int16); ok {
-			dst.SetUint(uint64(i))
-			return nil
-		}
-		if i, ok := src.(int32); ok {
-			dst.SetUint(uint64(i))
-			return nil
-		}
-		return fmt.Errorf("cannot set reflect.Value of type %s from %T", dst.Kind(), src)
+			if i, ok := src.(uint64); ok {
+				dst.SetUint(i)
+				return nil
+			}
+			if i, ok := src.(uint); ok {
+				dst.SetUint(uint64(i))
+				return nil
+			}
+			if i, ok := src.(uint8); ok {
+				dst.SetUint(uint64(i))
+				return nil
+			}
+			if i, ok := src.(uint16); ok {
+				dst.SetUint(uint64(i))
+				return nil
+			}
+			if i, ok := src.(uint32); ok {
+				dst.SetUint(uint64(i))
+				return nil
+			}
+			if i, ok := src.(int64); ok {
+				dst.SetUint(uint64(i))
+				return nil
+			}
+			if i, ok := src.(int); ok {
+				dst.SetUint(uint64(i))
+				return nil
+			}
+			if i, ok := src.(int8); ok {
+				dst.SetUint(uint64(i))
+				return nil
+			}
+			if i, ok := src.(int16); ok {
+				dst.SetUint(uint64(i))
+				return nil
+			}
+			if i, ok := src.(int32); ok {
+				dst.SetUint(uint64(i))
+				return nil
+			}
+			return fmt.Errorf("cannot set reflect.Value of type %s from %T", kind, src)
 
-	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
-		if s, ok := src.(string); ok {
-			i, err := strconv.ParseInt(s, 10, 64)
-			if err != nil {
-				return err
+		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+			if s, ok := src.(string); ok {
+				i, err := strconv.ParseInt(s, 10, 64)
+				if err != nil {
+					return err
+				}
+				dst.SetInt(i)
+				return nil
+			}
+			if i, ok := src.(int64); ok {
+				dst.SetInt(i)
+				return nil
+			}
+			if i, ok := src.(int); ok {
+				dst.SetInt(int64(i))
+				return nil
+			}
+			if i, ok := src.(int8); ok {
+				dst.SetInt(int64(i))
+				return nil
+			}
+			if i, ok := src.(int16); ok {
+				dst.SetInt(int64(i))
+				return nil
+			}
+			if i, ok := src.(int32); ok {
+				dst.SetInt(int64(i))
+				return nil
 			}
-			dst.SetInt(i)
-			return nil
-		}
-		if i, ok := src.(int64); ok {
-			dst.SetInt(i)
-			return nil
-		}
-		if i, ok := src.(int); ok {
-			dst.SetInt(int64(i))
-			return nil
-		}
-		if i, ok := src.(int8); ok {
-			dst.SetInt(int64(i))
-			return nil
-		}
-		if i, ok := src.(int16); ok {
-			dst.SetInt(int64(i))
-			return nil
-		}
-		if i, ok := src.(int32); ok {
-			dst.SetInt(int64(i))
-			return nil
-		}
 
-		if i, ok := src.(uint64); ok {
-			dst.SetInt(int64(i))
-			return nil
-		}
-		if i, ok := src.(uint); ok {
-			dst.SetInt(int64(i))
-			return nil
-		}
-		if i, ok := src.(uint8); ok {
-			dst.SetInt(int64(i))
-			return nil
-		}
-		if i, ok := src.(uint16); ok {
-			dst.SetInt(int64(i))
-			return nil
-		}
-		if i, ok := src.(uint32); ok {
-			dst.SetInt(int64(i))
-			return nil
-		}
-		return fmt.Errorf("cannot set reflect.Value of type %s from %T", dst.Kind(), src)
+			if i, ok := src.(uint64); ok {
+				dst.SetInt(int64(i))
+				return nil
+			}
+			if i, ok := src.(uint); ok {
+				dst.SetInt(int64(i))
+				return nil
+			}
+			if i, ok := src.(uint8); ok {
+				dst.SetInt(int64(i))
+				return nil
+			}
+			if i, ok := src.(uint16); ok {
+				dst.SetInt(int64(i))
+				return nil
+			}
+			if i, ok := src.(uint32); ok {
+				dst.SetInt(int64(i))
+				return nil
+			}
+			return fmt.Errorf("cannot set reflect.Value of type %s from %T", kind, src)
 
-	case reflect.Float32, reflect.Float64:
-		if f, ok := src.(float64); ok {
-			dst.SetFloat(f)
-			return nil
-		}
-		if f, ok := src.(float32); ok {
-			dst.SetFloat(float64(f))
-			return nil
-		}
-		if i, ok := src.(int64); ok {
-			dst.SetFloat(float64(i))
-			return nil
-		}
-		if i, ok := src.(int); ok {
-			dst.SetFloat(float64(i))
-			return nil
-		}
-		if i, ok := src.(int8); ok {
-			dst.SetFloat(float64(i))
-			return nil
-		}
-		if i, ok := src.(int16); ok {
-			dst.SetFloat(float64(i))
-			return nil
-		}
-		if i, ok := src.(int32); ok {
-			dst.SetFloat(float64(i))
-			return nil
-		}
-		if i, ok := src.(uint64); ok {
-			dst.SetFloat(float64(i))
-			return nil
-		}
-		if i, ok := src.(uint); ok {
-			dst.SetFloat(float64(i))
-			return nil
-		}
-		if i, ok := src.(uint8); ok {
-			dst.SetFloat(float64(i))
-			return nil
-		}
-		if i, ok := src.(uint16); ok {
-			dst.SetFloat(float64(i))
-			return nil
-		}
-		if i, ok := src.(uint32); ok {
-			dst.SetFloat(float64(i))
-			return nil
-		}
-		return fmt.Errorf("cannot set reflect.Value of type %s from %T", dst.Kind(), src)
-	case reflect.Bool:
-		if b, ok := src.(bool); ok {
-			dst.SetBool(b)
-			return nil
-		}
-		return fmt.Errorf("cannot set reflect.Value of type %s from %T", dst.Kind(), src)
-	case reflect.Slice:
-		//exhaustive:ignore
-		sliceKind := dst.Type().Elem().Kind()
-		//exhaustive:ignore
-		switch sliceKind {
-		case reflect.String:
-			if s, ok := src.([]string); ok {
-				dst.Set(reflect.ValueOf(s))
+		case reflect.Float32, reflect.Float64:
+			if f, ok := src.(float64); ok {
+				dst.SetFloat(f)
 				return nil
 			}
-			if s, ok := src.([]interface{}); ok {
-				v2_, ok := cast.CastList[string, interface{}](s)
-				if !ok {
-					return fmt.Errorf("cannot cast %T to []string", src)
-				}
-				dst.Set(reflect.ValueOf(v2_))
+			if f, ok := src.(float32); ok {
+				dst.SetFloat(float64(f))
+				return nil
+			}
+			if i, ok := src.(int64); ok {
+				dst.SetFloat(float64(i))
+				return nil
+			}
+			if i, ok := src.(int); ok {
+				dst.SetFloat(float64(i))
+				return nil
+			}
+			if i, ok := src.(int8); ok {
+				dst.SetFloat(float64(i))
+				return nil
+			}
+			if i, ok := src.(int16); ok {
+				dst.SetFloat(float64(i))
+				return nil
+			}
+			if i, ok := src.(int32); ok {
+				dst.SetFloat(float64(i))
+				return nil
+			}
+			if i, ok := src.(uint64); ok {
+				dst.SetFloat(float64(i))
+				return nil
+			}
+			if i, ok := src.(uint); ok {
+				dst.SetFloat(float64(i))
+				return nil
+			}
+			if i, ok := src.(uint8); ok {
+				dst.SetFloat(float64(i))
+				return nil
+			}
+			if i, ok := src.(uint16); ok {
+				dst.SetFloat(float64(i))
+				return nil
+			}
+			if i, ok := src.(uint32); ok {
+				dst.SetFloat(float64(i))
 				return nil
 			}
+			return fmt.Errorf("cannot set reflect.Value of type %s from %T", kind, src)
+		case reflect.Bool:
+			if b, ok := src.(bool); ok {
+				dst.SetBool(b)
+				return nil
+			}
+			return fmt.Errorf("cannot set reflect.Value of type %s from %T", kind, src)
+		case reflect.Slice:
+			//exhaustive:ignore
+			sliceKind := dst.Type().Elem().Kind()
+			//exhaustive:ignore
+			switch sliceKind {
+			case reflect.String:
+				if s, ok := src.([]string); ok {
+					dst.Set(reflect.ValueOf(s))
+					return nil
+				}
+				if s, ok := src.([]interface{}); ok {
+					v2_, ok := cast.CastList[string, interface{}](s)
+					if !ok {
+						return fmt.Errorf("cannot cast %T to []string", src)
+					}
+					dst.Set(reflect.ValueOf(v2_))
+					return nil
+				}
 
-			return fmt.Errorf("cannot set reflect.Value of type %s from %T", dst.Kind(), src)
+				if s, ok := src.(*interface{}); ok {
+					s_, ok := (*s).([]interface{})
+					if !ok {
+						return fmt.Errorf("cannot cast %T to []string", src)
+					}
+					v2_, ok := cast.CastList[string, interface{}](s_)
+					if !ok {
+						return fmt.Errorf("cannot cast %T to []string", src)
+					}
+					dst.Set(reflect.ValueOf(v2_))
+					return nil
+				}
 
-		case reflect.Int:
-			return SetIntListReflectValue[int](dst, src)
+				return fmt.Errorf("cannot set reflect.Value of type %s from %T", kind, src)
 
-		case reflect.Int8:
-			return SetIntListReflectValue[int8](dst, src)
+			case reflect.Int:
+				return SetIntListReflectValue[int](dst, src)
 
-		case reflect.Int16:
-			return SetIntListReflectValue[int16](dst, src)
+			case reflect.Int8:
+				return SetIntListReflectValue[int8](dst, src)
 
-		case reflect.Int32:
-			return SetIntListReflectValue[int32](dst, src)
+			case reflect.Int16:
+				return SetIntListReflectValue[int16](dst, src)
 
-		case reflect.Int64:
-			return SetIntListReflectValue[int64](dst, src)
+			case reflect.Int32:
+				return SetIntListReflectValue[int32](dst, src)
 
-		case reflect.Uint:
-			return SetIntListReflectValue[uint](dst, src)
+			case reflect.Int64:
+				return SetIntListReflectValue[int64](dst, src)
 
-		case reflect.Uint8:
-			return SetIntListReflectValue[uint8](dst, src)
+			case reflect.Uint:
+				return SetIntListReflectValue[uint](dst, src)
 
-		case reflect.Uint16:
-			return SetIntListReflectValue[uint16](dst, src)
+			case reflect.Uint8:
+				return SetIntListReflectValue[uint8](dst, src)
 
-		case reflect.Uint32:
-			return SetIntListReflectValue[uint32](dst, src)
+			case reflect.Uint16:
+				return SetIntListReflectValue[uint16](dst, src)
 
-		case reflect.Uint64:
-			return SetIntListReflectValue[uint64](dst, src)
+			case reflect.Uint32:
+				return SetIntListReflectValue[uint32](dst, src)
 
-		case reflect.Float32:
-			return SetFloatListReflectValue[float32](dst, src)
+			case reflect.Uint64:
+				return SetIntListReflectValue[uint64](dst, src)
 
-		case reflect.Float64:
-			return SetFloatListReflectValue[float64](dst, src)
+			case reflect.Float32:
+				return SetFloatListReflectValue[float32](dst, src)
 
-		default:
-			return fmt.Errorf("cannot set reflect.Value of type %s from %T", dst.Kind(), src)
-		}
+			case reflect.Float64:
+				return SetFloatListReflectValue[float64](dst, src)
 
-	case reflect.Map:
-		//exhaustive:ignore
-		switch dst.Type().Elem().Kind() {
-		case reflect.String:
-			if m, ok := src.(map[string]string); ok {
-				dst.Set(reflect.ValueOf(m))
-				return nil
-			}
+			case reflect.Map:
+				return SetStringMapListReflectValue[interface{}](dst, src)
+
+			default:
+				// try to cast each element of src into type of dst
+				dstElemType := dst.Type().Elem()
+				srcVal := reflect.ValueOf(src)
+				newSlice := reflect.MakeSlice(reflect.SliceOf(dstElemType), srcVal.Len(), srcVal.Cap())
+
+				for i := 0; i < srcVal.Len(); i++ {
+					srcElem := srcVal.Index(i)
+					if srcElem.Kind() == reflect.Interface && !srcElem.IsNil() {
+						srcElem = srcElem.Elem()
+					}
+					dstElem := reflect.New(dstElemType).Elem()
 
-			if m, ok := src.(map[string]interface{}); ok {
-				v2_, ok := cast.CastStringMap[string, interface{}](m)
-				if !ok {
-					return fmt.Errorf("cannot cast %T to map[string]string", src)
+					if srcElem.Type().AssignableTo(dstElemType) {
+						dstElem.Set(srcElem)
+					} else if srcElem.Type().ConvertibleTo(dstElemType) {
+						dstElem.Set(srcElem.Convert(dstElemType))
+					} else {
+						return fmt.Errorf("cannot convert element %d of type %s to %s", i, srcElem.Type(), dstElemType)
+					}
+
+					newSlice.Index(i).Set(dstElem)
 				}
-				dst.Set(reflect.ValueOf(v2_))
-				return nil
-			}
 
-		case reflect.Interface:
-			if m, ok := src.(map[string]interface{}); ok {
-				dst.Set(reflect.ValueOf(m))
+				dst.Set(newSlice)
 				return nil
 			}
 
-			if m, ok := src.(map[string]string); ok {
-				v2_, ok := cast.CastStringMap[interface{}, string](m)
-				if !ok {
-					return fmt.Errorf("cannot cast %T to map[string]interface{}", src)
+		case reflect.Map:
+			//exhaustive:ignore
+			switch dst.Type().Elem().Kind() {
+			case reflect.String:
+				if m, ok := src.(map[string]string); ok {
+					dst.Set(reflect.ValueOf(m))
+					return nil
+				}
+
+				if m, ok := src.(map[string]interface{}); ok {
+					v2_, ok := cast.CastStringMap[string, interface{}](m)
+					if !ok {
+						return fmt.Errorf("cannot cast %T to map[string]string", src)
+					}
+					dst.Set(reflect.ValueOf(v2_))
+					return nil
 				}
-				dst.Set(reflect.ValueOf(v2_))
-				return nil
 
+			case reflect.Interface:
+				if m, ok := src.(map[string]interface{}); ok {
+					dst.Set(reflect.ValueOf(m))
+					return nil
+				}
+
+				if m, ok := src.(map[string]string); ok {
+					v2_, ok := cast.CastStringMap[interface{}, string](m)
+					if !ok {
+						return fmt.Errorf("cannot cast %T to map[string]interface{}", src)
+					}
+					dst.Set(reflect.ValueOf(v2_))
+					return nil
+
+				}
+
+			default:
+				return fmt.Errorf("cannot set reflect.Value of type %s from %T", kind, src)
 			}
+			return fmt.Errorf("cannot set reflect.Value of type %s from %T", kind, src)
 
 		default:
-			return fmt.Errorf("cannot set reflect.Value of type %s from %T", dst.Kind(), src)
+			return fmt.Errorf("unsupported reflect.Value type %s", kind)
 		}
-		return fmt.Errorf("cannot set reflect.Value of type %s from %T", dst.Kind(), src)
 
-	default:
-		return fmt.Errorf("unsupported reflect.Value type %s", dst.Kind())
 	}
+
+	err := setValue(src)
+	if err != nil {
+		// try again, checking if src is an *interface{} and unwrapping that
+		if i, ok := src.(*interface{}); ok {
+			src = *i
+			err = setValue(src)
+			if err != nil {
+				return err
+			}
+		} else {
+			return err
+		}
+	}
+
+	return nil
 }
 
 func SetIntListReflectValue[To cast.Number](value reflect.Value, v interface{}) error {
@@ -431,3 +491,16 @@ func SetFloatListReflectValue[To cast.FloatNumber](value reflect.Value, v interf
 
 	return SetIntListReflectValue[To](value, v)
 }
+
+func SetStringMapListReflectValue[To interface{}](mapSlice reflect.Value, v interface{}) error {
+	keyKind := mapSlice.Type().Elem().Key().Kind()
+	if keyKind == reflect.String {
+		r, ok := cast.CastList2[map[string]interface{}, interface{}](v)
+		if !ok {
+			return fmt.Errorf("cannot cast %T to []map[string]interface{}", v)
+		}
+		mapSlice.Set(reflect.ValueOf(r))
+		return nil
+	}
+	return fmt.Errorf("cannot set reflect.Value of type %s from %T", keyKind, v)
+}
diff --git a/prompto/glazed/create-command.yaml b/prompto/glazed/create-command.yaml
index bc89b62..0f2337a 100644
--- a/prompto/glazed/create-command.yaml
+++ b/prompto/glazed/create-command.yaml
@@ -252,7 +252,7 @@ template: |
       Command
       RunIntoWriter(
           ctx context.Context,
-          ps map[string]interface{},
+          ps *layers.ParsedLayers,
           w io.Writer,
       ) error
   }


Test patch:
diff --git a/pkg/cmds/middlewares/middlewares_test.go b/pkg/cmds/middlewares/middlewares_test.go
index 863feed..dbb6f40 100644
--- a/pkg/cmds/middlewares/middlewares_test.go
+++ b/pkg/cmds/middlewares/middlewares_test.go
@@ -38,7 +38,7 @@ func TestSetFromDefaults(t *testing.T) {
 			layers_ := helpers.NewTestParameterLayers(tt.ParameterLayers)
 			parsedLayers := helpers.NewTestParsedLayers(layers_, tt.ParsedLayers...)
 
-			middleware := middlewares.SetFromDefaults(parameters.WithParseStepSource("defaults"))
+			middleware := middlewares.SetFromDefaults(parameters.WithParseStepSource(parameters.SourceDefaults))
 			err := middleware(func(layers *layers.ParameterLayers, parsedLayers *layers.ParsedLayers) error {
 				return nil
 			})(layers_, parsedLayers)
diff --git a/pkg/cmds/parameters/initialize-struct_test.go b/pkg/cmds/parameters/initialize-struct_test.go
index 15a2af3..e85f87b 100644
--- a/pkg/cmds/parameters/initialize-struct_test.go
+++ b/pkg/cmds/parameters/initialize-struct_test.go
@@ -2,6 +2,7 @@ package parameters_test
 
 import (
 	"github.com/go-go-golems/glazed/pkg/cmds/parameters" // Replace with the actual package path
+	"github.com/stretchr/testify/require"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
@@ -15,7 +16,6 @@ type TestStruct struct {
 
 // TestInitializeStructWithValidStruct tests initializing a struct with valid parameters
 func TestInitializeStructWithValidStruct(t *testing.T) {
-	// Create an instance of ParsedParameters with test data
 	parsedParams := parameters.NewParsedParameters(
 		parameters.WithParsedParameter(
 			parameters.NewParameterDefinition(
@@ -30,13 +30,10 @@ func TestInitializeStructWithValidStruct(t *testing.T) {
 			"age", 30),
 	)
 
-	// Create an instance of the struct to be initialized
 	testStruct := &TestStruct{}
 
-	// Call InitializeStruct with the test struct and parsed parameters
 	err := parsedParams.InitializeStruct(testStruct)
 
-	// Assert that there is no error and the struct is initialized correctly
 	assert.NoError(t, err)
 	assert.Equal(t, "John Doe", testStruct.Name)
 	assert.Equal(t, 30, testStruct.Age)
@@ -44,10 +41,8 @@ func TestInitializeStructWithValidStruct(t *testing.T) {
 
 // TestInitializeStructWithNilInput tests initializing a struct with a nil input
 func TestInitializeStructWithNilInput(t *testing.T) {
-	// Create an instance of ParsedParameters (can be empty for this test)
 	parsedParams := &parameters.ParsedParameters{}
 
-	// Call InitializeStruct with a nil pointer
 	err := parsedParams.InitializeStruct(nil)
 
 	assert.Error(t, err)
@@ -56,35 +51,28 @@ func TestInitializeStructWithNilInput(t *testing.T) {
 
 // TestInitializeStructWithNonPointerInput tests initializing a struct with a non-pointer input
 func TestInitializeStructWithNonPointerInput(t *testing.T) {
-	// Create an instance of ParsedParameters (can be empty for this test)
 	parsedParams := &parameters.ParsedParameters{}
 
-	// Call InitializeStruct with a non-pointer value (struct value)
 	testStruct := TestStruct{}
 	err := parsedParams.InitializeStruct(testStruct)
 
-	// Assert that an error is returned and the error message is correct
 	assert.Error(t, err)
 	assert.Equal(t, "s is not a pointer", err.Error())
 }
 
 // TestInitializeStructWithNonStructPointer tests initializing a struct with a pointer to a non-struct type
 func TestInitializeStructWithNonStructPointer(t *testing.T) {
-	// Create an instance of ParsedParameters (can be empty for this test)
 	parsedParams := &parameters.ParsedParameters{}
 
-	// Call InitializeStruct with a pointer to a non-struct type (e.g., string)
 	nonStruct := "I am not a struct"
 	err := parsedParams.InitializeStruct(&nonStruct)
 
-	// Assert that an error is returned and the error message is correct
 	assert.Error(t, err)
 	assert.Equal(t, "s is not a pointer to a struct", err.Error())
 }
 
 // TestInitializeStructWithMissingParameters tests initializing a struct with missing parameters
 func TestInitializeStructWithMissingParameters(t *testing.T) {
-	// Create an instance of ParsedParameters with only one parameter defined
 	parsedParams := parameters.NewParsedParameters(
 		parameters.WithParsedParameter(
 			parameters.NewParameterDefinition(
@@ -94,13 +82,10 @@ func TestInitializeStructWithMissingParameters(t *testing.T) {
 			"John Doe"),
 	)
 
-	// Create an instance of the struct to be initialized
 	testStruct := &TestStruct{}
 
-	// Call InitializeStruct with the test struct and parsed parameters
 	err := parsedParams.InitializeStruct(testStruct)
 
-	// Assert that there is no error and the struct is partially initialized correctly
 	assert.NoError(t, err)
 	assert.Equal(t, "John Doe", testStruct.Name)
 	assert.Equal(t, 0, testStruct.Age) // Age should be the zero value since it's missing from parsedParams
@@ -108,12 +93,10 @@ func TestInitializeStructWithMissingParameters(t *testing.T) {
 
 // TestInitializeStructWithJSONTagOnNonPointerField tests initializing a struct with a `from_json` tag on a non-pointer field
 func TestInitializeStructWithJSONTagOnNonPointerField(t *testing.T) {
-	// Define a struct with a `from_json` tag on a non-pointer field
 	type TestStructWithJSONTag struct {
 		Config string `glazed.parameter:"config,from_json"`
 	}
 
-	// Create an instance of ParsedParameters with a JSON parameter
 	parsedParams := parameters.NewParsedParameters(
 		parameters.WithParsedParameter(
 			parameters.NewParameterDefinition(
@@ -123,25 +106,20 @@ func TestInitializeStructWithJSONTagOnNonPointerField(t *testing.T) {
 			`{"key": "value"}`),
 	)
 
-	// Create an instance of the struct to be initialized
 	testStruct := &TestStructWithJSONTag{}
 
-	// Call InitializeStruct with the test struct and parsed parameters
 	err := parsedParams.InitializeStruct(testStruct)
 
-	// Assert that an error is returned and the error message is correct
 	assert.Error(t, err)
 	assert.Contains(t, err.Error(), "from_json tag can only be used on pointer fields")
 }
 
 // TestParseStringListSuccessfully tests parsing a string list successfully
 func TestParseStringListSuccessfully(t *testing.T) {
-	// Define a struct that matches the expected structure for InitializeStruct
 	type TestStruct struct {
 		Tags []string `glazed.parameter:"tags"`
 	}
 
-	// Create an instance of ParsedParameters with test data
 	parsedParams := parameters.NewParsedParameters(
 		parameters.WithParsedParameter(
 			parameters.NewParameterDefinition(
@@ -151,25 +129,20 @@ func TestParseStringListSuccessfully(t *testing.T) {
 			[]string{"go", "testing", "glazed"}),
 	)
 
-	// Create an instance of the struct to be initialized
 	testStruct := &TestStruct{}
 
-	// Call InitializeStruct with the test struct and parsed parameters
 	err := parsedParams.InitializeStruct(testStruct)
 
-	// Assert that there is no error and the struct is initialized correctly
 	assert.NoError(t, err)
 	assert.Equal(t, []string{"go", "testing", "glazed"}, testStruct.Tags)
 }
 
 // TestParseIntListSuccessfully tests parsing an integer list successfully
 func TestParseIntListSuccessfully(t *testing.T) {
-	// Define a struct that matches the expected structure for InitializeStruct
 	type TestStruct struct {
 		Numbers []int `glazed.parameter:"numbers"`
 	}
 
-	// Create an instance of ParsedParameters with test data
 	parsedParams := parameters.NewParsedParameters(
 		parameters.WithParsedParameter(
 			parameters.NewParameterDefinition(
@@ -179,25 +152,20 @@ func TestParseIntListSuccessfully(t *testing.T) {
 			[]int{1, 2, 3, 4, 5}),
 	)
 
-	// Create an instance of the struct to be initialized
 	testStruct := &TestStruct{}
 
-	// Call InitializeStruct with the test struct and parsed parameters
 	err := parsedParams.InitializeStruct(testStruct)
 
-	// Assert that there is no error and the struct is initialized correctly
 	assert.NoError(t, err)
 	assert.Equal(t, []int{1, 2, 3, 4, 5}, testStruct.Numbers)
 }
 
 // TestParseObjectFromFileSuccessfully tests parsing an object from file successfully
 func TestParseObjectFromFileSuccessfully(t *testing.T) {
-	// Define a struct that matches the expected structure for InitializeStruct
 	type TestStruct struct {
 		Config map[string]interface{} `glazed.parameter:"config"`
 	}
 
-	// Create an instance of ParsedParameters with test data
 	parsedParams := parameters.NewParsedParameters(
 		parameters.WithParsedParameter(
 			parameters.NewParameterDefinition(
@@ -210,13 +178,10 @@ func TestParseObjectFromFileSuccessfully(t *testing.T) {
 			}),
 	)
 
-	// Create an instance of the struct to be initialized
 	testStruct := &TestStruct{}
 
-	// Call InitializeStruct with the test struct and parsed parameters
 	err := parsedParams.InitializeStruct(testStruct)
 
-	// Assert that there is no error and the struct is initialized correctly
 	assert.NoError(t, err)
 	expectedConfig := map[string]interface{}{
 		"key1": "value1",
@@ -245,7 +210,7 @@ func TestInitializeStructWithInvalidJSON(t *testing.T) {
 	err := parsedParams.InitializeStruct(testStruct)
 
 	assert.Error(t, err)
-	assert.Contains(t, err.Error(), "failed to unmarshal json for config")
+	assert.Contains(t, err.Error(), "failed to unmarshal JSON")
 }
 
 // TestInitializeStructWithUnsupportedTypeForJSON tests initializing a struct with an unsupported type for JSON
@@ -270,3 +235,175 @@ func TestInitializeStructWithUnsupportedTypeForJSON(t *testing.T) {
 	assert.Error(t, err)
 	assert.Contains(t, err.Error(), "from_json tag can only be used on pointer fields")
 }
+
+// TestStruct for wildcard functionality
+type TestStructWithWildcard struct {
+	ApiKeys map[string]string `glazed.parameter:"*_api_key"`
+}
+
+// TestInitializeStructWithWildcardMultipleMatches tests wildcard pattern matching multiple parameters
+func TestInitializeStructWithWildcardMultipleMatches(t *testing.T) {
+	parsedParams := parameters.NewParsedParameters(
+		parameters.WithParsedParameter(
+			parameters.NewParameterDefinition(
+				"openai_api_key",
+				parameters.ParameterTypeString),
+			"openai_api_key",
+			"openai-secret"),
+		parameters.WithParsedParameter(
+			parameters.NewParameterDefinition(
+				"google_api_key",
+				parameters.ParameterTypeString),
+			"google_api_key",
+			"google-secret"),
+	)
+
+	testStruct := &TestStructWithWildcard{}
+	err := parsedParams.InitializeStruct(testStruct)
+
+	require.NoError(t, err)
+	assert.Equal(t, map[string]string{
+		"openai_api_key": "openai-secret",
+		"google_api_key": "google-secret",
+	}, testStruct.ApiKeys)
+}
+
+// TestInitializeStructWithWildcardNoMatches tests wildcard pattern matching no parameters
+func TestInitializeStructWithWildcardNoMatches(t *testing.T) {
+	parsedParams := parameters.NewParsedParameters()
+
+	testStruct := &TestStructWithWildcard{}
+	err := parsedParams.InitializeStruct(testStruct)
+
+	require.NoError(t, err)
+	assert.Empty(t, testStruct.ApiKeys)
+}
+
+// TestInitializeStructWithWildcardOnNonMapField tests wildcard pattern used on a non-map field
+func TestInitializeStructWithWildcardOnNonMapField(t *testing.T) {
+	type TestStructNonMapWildcard struct {
+		ApiKeys string `glazed.parameter:"*_api_key"`
+	}
+
+	parsedParams := parameters.NewParsedParameters(
+		parameters.WithParsedParameter(
+			parameters.NewParameterDefinition(
+				"openai_api_key",
+				parameters.ParameterTypeString),
+			"openai_api_key",
+			"openai-secret"),
+	)
+
+	testStruct := &TestStructNonMapWildcard{}
+	err := parsedParams.InitializeStruct(testStruct)
+
+	assert.Error(t, err)
+	assert.Contains(t, err.Error(), "wildcard parameters require a map field")
+}
+
+// TestInitializeStructWithWildcardOnMapWithIncorrectValueTypes tests wildcard pattern on a map with incorrect value types
+func TestInitializeStructWithWildcardOnMapWithIncorrectValueTypes(t *testing.T) {
+	type TestStructMapWildcardIncorrectTypes struct {
+		ApiKeys map[string]int `glazed.parameter:"*_api_key"`
+	}
+
+	parsedParams := parameters.NewParsedParameters(
+		parameters.WithParsedParameter(
+			parameters.NewParameterDefinition(
+				"openai_api_key",
+				parameters.ParameterTypeString),
+			"openai_api_key",
+			"openai-secret"),
+		parameters.WithParsedParameter(
+			parameters.NewParameterDefinition(
+				"google_api_key",
+				parameters.ParameterTypeString),
+			"google_api_key",
+			"google-secret"),
+	)
+
+	testStruct := &TestStructMapWildcardIncorrectTypes{}
+	err := parsedParams.InitializeStruct(testStruct)
+
+	assert.Error(t, err)
+	assert.Contains(t, err.Error(), "failed to set wildcard values for ")
+}
+
+// TestInitializeStructWithWildcardOnMapWithCorrectValueTypes tests wildcard pattern on a map with correct value types
+func TestInitializeStructWithWildcardOnMapWithCorrectValueTypes(t *testing.T) {
+	parsedParams := parameters.NewParsedParameters(
+		parameters.WithParsedParameter(
+			parameters.NewParameterDefinition(
+				"openai_api_key",
+				parameters.ParameterTypeString),
+			"openai_api_key",
+			"openai-secret"),
+	)
+
+	testStruct := &TestStructWithWildcard{}
+	err := parsedParams.InitializeStruct(testStruct)
+
+	require.NoError(t, err)
+	assert.Equal(t, map[string]string{
+		"openai_api_key": "openai-secret",
+	}, testStruct.ApiKeys)
+}
+
+// TestInitializeStructWithWildcardComplexPatterns tests wildcard pattern with complex patterns
+func TestInitializeStructWithWildcardComplexPatterns(t *testing.T) {
+	type TestStructComplexWildcard struct {
+		ApiKeys map[string]string `glazed.parameter:"*api_key"`
+	}
+
+	parsedParams := parameters.NewParsedParameters(
+		parameters.WithParsedParameter(
+			parameters.NewParameterDefinition(
+				"openai_api_key",
+				parameters.ParameterTypeString),
+			"openai_api_key",
+			"openai-secret"),
+		parameters.WithParsedParameter(
+			parameters.NewParameterDefinition(
+				"google_api_key",
+				parameters.ParameterTypeString),
+			"google_api_key",
+			"google-secret"),
+		parameters.WithParsedParameter(
+			parameters.NewParameterDefinition(
+				"api_key_unrelated",
+				parameters.ParameterTypeString),
+			"api_key_unrelated",
+			"should-not-match"),
+	)
+
+	testStruct := &TestStructComplexWildcard{}
+	err := parsedParams.InitializeStruct(testStruct)
+
+	require.NoError(t, err)
+	assert.Equal(t, map[string]string{
+		"openai_api_key": "openai-secret",
+		"google_api_key": "google-secret",
+	}, testStruct.ApiKeys)
+}
+
+// TestInitializeStructNormalBehavior tests the normal behavior without wildcards
+func TestInitializeStructNormalBehavior(t *testing.T) {
+	type TestStructNormal struct {
+		Name string `glazed.parameter:"name"`
+	}
+
+	parsedParams := parameters.NewParsedParameters(
+		parameters.WithParsedParameter(
+			parameters.NewParameterDefinition(
+				"name",
+				parameters.ParameterTypeString),
+			"name",
+			"John Doe"),
+	)
+
+	testStruct := &TestStructNormal{}
+	err := parsedParams.InitializeStruct(testStruct)
+
+	require.NoError(t, err)
+	assert.Equal(t, "John Doe", testStruct.Name)
+}
diff --git a/pkg/helpers/reflect/reflect_test.go b/pkg/helpers/reflect/reflect_test.go
index 798e7b2..0b5d3f1 100644
--- a/pkg/helpers/reflect/reflect_test.go
+++ b/pkg/helpers/reflect/reflect_test.go
@@ -526,3 +526,16 @@ func testSetReflectFloatList[T cast.FloatNumber](t *testing.T, sValue reflect.Va
 	err = SetReflectValue(sValue, []interface{}{1.1, "world"})
 	require.Error(t, err)
 }
+
+// Test that passing a list of strings as an interface{} gets unwrapped properly
+func TestSetReflectStringListInterface(t *testing.T) {
+	s := []string{}
+	sValue := reflect.ValueOf(&s).Elem()
+
+	err := SetReflectValue(sValue, []interface{}{"hello", "world"})
+	require.NoError(t, err)
+	assert.Equal(t, []string{"hello", "world"}, sValue.Interface())
+
+	err = SetReflectValue(sValue, []interface{}{"hello", 1})
+	require.Error(t, err)
+}
diff --git a/pkg/settings/settings_template_test.go b/pkg/settings/settings_template_test.go
index bba7b04..601436b 100644
--- a/pkg/settings/settings_template_test.go
+++ b/pkg/settings/settings_template_test.go
@@ -19,7 +19,7 @@ func makeAndParse(t *testing.T, defaults *TemplateFlagsDefaults, args ...string)
 	parsedLayers := layers.NewParsedLayers()
 	err = middlewares.ExecuteMiddlewares(layers_, parsedLayers,
 		middlewares.UpdateFromStringList("", args, parameters.WithParseStepSource("string-list")),
-		middlewares.SetFromDefaults(parameters.WithParseStepSource("defaults")),
+		middlewares.SetFromDefaults(parameters.WithParseStepSource(parameters.SourceDefaults)),
 	)
 	require.NoError(t, err)