Repository: gruntwork-io/terragrunt (language: go)

Issue:
Incorrect suggestion for deprecation warning `--terragrunt-no-auto-init` ==> `--no-auto-init=false` in v0.73.0
## Describe the bug

v0.73.0 is warning about the deprecation of `--terragrunt-no-auto-init`, which is fine, but is suggesting to replace it with `--no-auto-init=false`. That seems inverted... Should probably just be `--no-auto-init`.

```
17:32:39.959 WARN   The `--terragrunt-no-auto-init` flag is deprecated and will be removed in a future version. Use `--no-auto-init=false` instead.
```



Discussion / hints:

Fix patch:
diff --git a/cli/app.go b/cli/app.go
index 2384a9019a..eb138fc32a 100644
--- a/cli/app.go
+++ b/cli/app.go
@@ -8,29 +8,21 @@ import (
 	"path/filepath"
 	"strings"
 
-	"github.com/gruntwork-io/terragrunt/cli/commands/info"
-	"github.com/gruntwork-io/terragrunt/cli/commands/stack"
-	"github.com/gruntwork-io/terragrunt/cli/flags"
+	"slices"
 
 	"github.com/gruntwork-io/terragrunt/engine"
 	"github.com/gruntwork-io/terragrunt/internal/os/exec"
 	"github.com/gruntwork-io/terragrunt/internal/os/signal"
 	"github.com/gruntwork-io/terragrunt/telemetry"
 	"github.com/gruntwork-io/terragrunt/tf"
-	"golang.org/x/exp/slices"
 	"golang.org/x/sync/errgroup"
 	"golang.org/x/text/cases"
 	"golang.org/x/text/language"
 
 	"github.com/gruntwork-io/terragrunt/cli/commands"
 	"github.com/gruntwork-io/terragrunt/cli/commands/graph"
-	"github.com/gruntwork-io/terragrunt/cli/commands/hclvalidate"
-	helpCmd "github.com/gruntwork-io/terragrunt/cli/commands/help"
-	versionCmd "github.com/gruntwork-io/terragrunt/cli/commands/version"
 	"github.com/gruntwork-io/terragrunt/cli/flags/global"
 
-	"github.com/gruntwork-io/terragrunt/cli/commands/scaffold"
-
 	"github.com/gruntwork-io/go-commons/version"
 	"github.com/gruntwork-io/terragrunt/config"
 	"github.com/gruntwork-io/terragrunt/internal/errors"
@@ -38,30 +30,13 @@ import (
 	hashicorpversion "github.com/hashicorp/go-version"
 
 	"github.com/gruntwork-io/go-commons/env"
-	awsproviderpatch "github.com/gruntwork-io/terragrunt/cli/commands/aws-provider-patch"
-	"github.com/gruntwork-io/terragrunt/cli/commands/catalog"
-	execCmd "github.com/gruntwork-io/terragrunt/cli/commands/exec"
-	graphdependencies "github.com/gruntwork-io/terragrunt/cli/commands/graph-dependencies"
-	"github.com/gruntwork-io/terragrunt/cli/commands/hclfmt"
-	outputmodulegroups "github.com/gruntwork-io/terragrunt/cli/commands/output-module-groups"
-	renderjson "github.com/gruntwork-io/terragrunt/cli/commands/render-json"
 	runCmd "github.com/gruntwork-io/terragrunt/cli/commands/run"
 	runall "github.com/gruntwork-io/terragrunt/cli/commands/run-all"
-	terragruntinfo "github.com/gruntwork-io/terragrunt/cli/commands/terragrunt-info"
-	validateinputs "github.com/gruntwork-io/terragrunt/cli/commands/validate-inputs"
 	"github.com/gruntwork-io/terragrunt/internal/cli"
 	"github.com/gruntwork-io/terragrunt/options"
 	"github.com/gruntwork-io/terragrunt/pkg/log/format/placeholders"
 )
 
-// Command category names.
-const (
-	MainCommandsCategoryName          = "Main commands"
-	CatalogCommandsCategoryName       = "Catalog commands"
-	ConfigurationCommandsCategoryName = "Configuration commands"
-	ShortcutsCommandsCategoryName     = "OpenTofu shortcuts"
-)
-
 func init() {
 	cli.AppVersionTemplate = AppVersionTemplate
 	cli.AppHelpTemplate = AppHelpTemplate
@@ -82,8 +57,8 @@ func NewApp(opts *options.TerragruntOptions) *App {
 	app.Version = version.GetVersion()
 	app.Writer = opts.Writer
 	app.ErrWriter = opts.ErrWriter
-	app.Flags = GlobalFlags(opts)
-	app.Commands = TerragruntCommands(opts).WrapAction(WrapWithTelemetry(opts))
+	app.Flags = global.NewFlagsWithDeprecatedMovedFlags(opts)
+	app.Commands = commands.New(opts).WrapAction(WrapWithTelemetry(opts))
 	app.Before = beforeAction(opts)
 	app.OsExiter = OSExiter
 	app.ExitErrHandler = ExitErrHandler
@@ -150,40 +125,6 @@ func (app *App) RunContext(ctx context.Context, args []string) error {
 	return nil
 }
 
-// GlobalFlags returns global flags. For backward compatibility, the slice contains flags that have been moved to other commands and are hidden from the CLI help.
-func GlobalFlags(opts *options.TerragruntOptions) cli.Flags {
-	globalFlags := global.NewFlags(opts, nil)
-
-	commands := cli.Commands{
-		runCmd.NewCommand(opts),             // run
-		runall.NewCommand(opts),             // runAction-all
-		terragruntinfo.NewCommand(opts),     // terragrunt-info
-		validateinputs.NewCommand(opts),     // validate-inputs
-		graphdependencies.NewCommand(opts),  // graph-dependencies
-		renderjson.NewCommand(opts),         // render-json
-		awsproviderpatch.NewCommand(opts),   // aws-provider-patch
-		outputmodulegroups.NewCommand(opts), // output-module-groups
-		graph.NewCommand(opts),              // graph
-	}
-
-	var seen []string
-
-	for _, cmd := range commands {
-		for _, flag := range cmd.Flags {
-			flagName := util.FirstElement(util.RemoveEmptyElements(flag.Names()))
-
-			if slices.Contains(seen, flagName) {
-				continue
-			}
-
-			seen = append(seen, flagName)
-			globalFlags = append(globalFlags, flags.NewMovedFlag(flag, cmd.Name, flags.StrictControlsByMovedGlobalFlags(opts.StrictControls, cmd.Name)))
-		}
-	}
-
-	return globalFlags
-}
-
 // removeNoColorFlagDuplicates removes one of the `--no-color` or `--terragrunt-no-color` arguments if both are present.
 // We have to do this because `--terragrunt-no-color` is a deprecated alias for `--no-color`,
 // therefore we end up specifying the same flag twice, which causes the `setting the flag multiple times` error.
@@ -208,66 +149,6 @@ func removeNoColorFlagDuplicates(args []string) []string {
 	return filteredArgs
 }
 
-// TerragruntCommands returns the set of Terragrunt commands.
-func TerragruntCommands(opts *options.TerragruntOptions) cli.Commands {
-	mainCommands := cli.Commands{
-		runall.NewCommand(opts),  // run-all
-		runCmd.NewCommand(opts),  // run
-		stack.NewCommand(opts),   // stack
-		graph.NewCommand(opts),   // graph
-		execCmd.NewCommand(opts), // exec
-	}.SetCategory(
-		&cli.Category{
-			Name:  MainCommandsCategoryName,
-			Order: 10, //nolint: mnd
-		},
-	)
-
-	catalogCommands := cli.Commands{
-		catalog.NewCommand(opts),  // catalog
-		scaffold.NewCommand(opts), // scaffold
-	}.SetCategory(
-		&cli.Category{
-			Name:  CatalogCommandsCategoryName,
-			Order: 20, //nolint: mnd
-		},
-	)
-
-	configurationCommands := cli.Commands{
-		graphdependencies.NewCommand(opts),  // graph-dependencies
-		outputmodulegroups.NewCommand(opts), // output-module-groups
-		validateinputs.NewCommand(opts),     // validate-inputs
-		hclvalidate.NewCommand(opts),        // hclvalidate
-		hclfmt.NewCommand(opts),             // hclfmt
-		info.NewCommand(opts),               // info
-		terragruntinfo.NewCommand(opts),     // terragrunt-info
-		renderjson.NewCommand(opts),         // render-json
-		helpCmd.NewCommand(opts),            // help (hidden)
-		versionCmd.NewCommand(opts),         // version (hidden)
-		awsproviderpatch.NewCommand(opts),   // aws-provider-patch (hidden)
-	}.SetCategory(
-		&cli.Category{
-			Name:  ConfigurationCommandsCategoryName,
-			Order: 30, //nolint: mnd
-		},
-	)
-
-	shortcutsCommands := commands.NewShortcutsCommands(opts).SetCategory(
-		&cli.Category{
-			Name:  ShortcutsCommandsCategoryName,
-			Order: 40, //nolint: mnd
-		},
-	)
-
-	allCommands := mainCommands.
-		Merge(catalogCommands...).
-		Merge(configurationCommands...).
-		Merge(shortcutsCommands...).
-		Merge(commands.NewDeprecatedCommands(opts)...)
-
-	return allCommands
-}
-
 // WrapWithTelemetry wraps CLI command execution with setting of telemetry context and labels, if telemetry is disabled, just runAction the command.
 func WrapWithTelemetry(opts *options.TerragruntOptions) func(ctx *cli.Context, action cli.ActionFunc) error {
 	return func(ctx *cli.Context, action cli.ActionFunc) error {
diff --git a/cli/commands/commands.go b/cli/commands/commands.go
index 356bcfea62..0157082fb4 100644
--- a/cli/commands/commands.go
+++ b/cli/commands/commands.go
@@ -1,2 +1,101 @@
 // Package commands represents CLI commands.
 package commands
+
+import (
+	"github.com/gruntwork-io/terragrunt/cli/commands/info"
+	"github.com/gruntwork-io/terragrunt/cli/commands/stack"
+	"github.com/gruntwork-io/terragrunt/options"
+
+	"github.com/gruntwork-io/terragrunt/cli/commands/graph"
+	"github.com/gruntwork-io/terragrunt/cli/commands/hclvalidate"
+	helpCmd "github.com/gruntwork-io/terragrunt/cli/commands/help"
+	versionCmd "github.com/gruntwork-io/terragrunt/cli/commands/version"
+
+	"github.com/gruntwork-io/terragrunt/cli/commands/scaffold"
+
+	awsproviderpatch "github.com/gruntwork-io/terragrunt/cli/commands/aws-provider-patch"
+	"github.com/gruntwork-io/terragrunt/cli/commands/catalog"
+	execCmd "github.com/gruntwork-io/terragrunt/cli/commands/exec"
+	graphdependencies "github.com/gruntwork-io/terragrunt/cli/commands/graph-dependencies"
+	"github.com/gruntwork-io/terragrunt/cli/commands/hclfmt"
+	outputmodulegroups "github.com/gruntwork-io/terragrunt/cli/commands/output-module-groups"
+	renderjson "github.com/gruntwork-io/terragrunt/cli/commands/render-json"
+	runCmd "github.com/gruntwork-io/terragrunt/cli/commands/run"
+	runall "github.com/gruntwork-io/terragrunt/cli/commands/run-all"
+	terragruntinfo "github.com/gruntwork-io/terragrunt/cli/commands/terragrunt-info"
+	validateinputs "github.com/gruntwork-io/terragrunt/cli/commands/validate-inputs"
+	"github.com/gruntwork-io/terragrunt/internal/cli"
+)
+
+// Command category names.
+const (
+	// MainCommandsCategoryName represents primary Terragrunt operations like run, run-all.
+	MainCommandsCategoryName = "Main commands"
+	// CatalogCommandsCategoryName represents commands for managing Terragrunt catalogs.
+	CatalogCommandsCategoryName = "Catalog commands"
+	// ConfigurationCommandsCategoryName represents commands for managing Terragrunt configurations.
+	ConfigurationCommandsCategoryName = "Configuration commands"
+	// ShortcutsCommandsCategoryName represents OpenTofu-specific shortcut commands.
+	ShortcutsCommandsCategoryName = "OpenTofu shortcuts"
+)
+
+// New returns the set of Terragrunt commands, grouped into categories.
+// Categories are ordered in increments of 10 for easy insertion of new categories.
+func New(opts *options.TerragruntOptions) cli.Commands {
+	mainCommands := cli.Commands{
+		runCmd.NewCommand(opts),  // run
+		runall.NewCommand(opts),  // run-all
+		stack.NewCommand(opts),   // stack
+		graph.NewCommand(opts),   // graph
+		execCmd.NewCommand(opts), // exec
+	}.SetCategory(
+		&cli.Category{
+			Name:  MainCommandsCategoryName,
+			Order: 10, //nolint: mnd
+		},
+	)
+
+	catalogCommands := cli.Commands{
+		catalog.NewCommand(opts),  // catalog
+		scaffold.NewCommand(opts), // scaffold
+	}.SetCategory(
+		&cli.Category{
+			Name:  CatalogCommandsCategoryName,
+			Order: 20, //nolint: mnd
+		},
+	)
+
+	configurationCommands := cli.Commands{
+		graphdependencies.NewCommand(opts),  // graph-dependencies
+		outputmodulegroups.NewCommand(opts), // output-module-groups
+		validateinputs.NewCommand(opts),     // validate-inputs
+		hclvalidate.NewCommand(opts),        // hclvalidate
+		hclfmt.NewCommand(opts),             // hclfmt
+		info.NewCommand(opts),               // info
+		terragruntinfo.NewCommand(opts),     // terragrunt-info
+		renderjson.NewCommand(opts),         // render-json
+		helpCmd.NewCommand(opts),            // help (hidden)
+		versionCmd.NewCommand(opts),         // version (hidden)
+		awsproviderpatch.NewCommand(opts),   // aws-provider-patch (hidden)
+	}.SetCategory(
+		&cli.Category{
+			Name:  ConfigurationCommandsCategoryName,
+			Order: 30, //nolint: mnd
+		},
+	)
+
+	shortcutsCommands := NewShortcutsCommands(opts).SetCategory(
+		&cli.Category{
+			Name:  ShortcutsCommandsCategoryName,
+			Order: 40, //nolint: mnd
+		},
+	)
+
+	allCommands := mainCommands.
+		Merge(catalogCommands...).
+		Merge(configurationCommands...).
+		Merge(shortcutsCommands...).
+		Merge(NewDeprecatedCommands(opts)...)
+
+	return allCommands
+}
diff --git a/cli/commands/help/command.go b/cli/commands/help/command.go
index e0b5cbd50f..dcade9a03f 100644
--- a/cli/commands/help/command.go
+++ b/cli/commands/help/command.go
@@ -2,9 +2,12 @@
 package help
 
 import (
-	"github.com/gruntwork-io/terragrunt/cli/flags/global"
+	"os"
+
 	"github.com/gruntwork-io/terragrunt/internal/cli"
+	"github.com/gruntwork-io/terragrunt/internal/errors"
 	"github.com/gruntwork-io/terragrunt/options"
+	"github.com/gruntwork-io/terragrunt/pkg/log"
 )
 
 const (
@@ -17,7 +20,46 @@ func NewCommand(opts *options.TerragruntOptions) *cli.Command {
 		Usage:  "Show help.",
 		Hidden: true,
 		Action: func(ctx *cli.Context) error {
-			return global.HelpAction(ctx, opts)
+			return Action(ctx, opts)
 		},
 	}
 }
+
+func Action(ctx *cli.Context, opts *options.TerragruntOptions) error {
+	var (
+		args = ctx.Args()
+		cmds = ctx.App.Commands
+	)
+
+	if opts.Logger.Level() >= log.DebugLevel {
+		// https: //github.com/urfave/cli/blob/f035ffaa3749afda2cd26fb824aa940747297ef1/help.go#L401
+		if err := os.Setenv("CLI_TEMPLATE_ERROR_DEBUG", "1"); err != nil {
+			return errors.Errorf("failed to set CLI_TEMPLATE_ERROR_DEBUG environment variable: %w", err)
+		}
+	}
+
+	if args.CommandName() == "" {
+		return cli.ShowAppHelp(ctx)
+	}
+
+	const maxCommandDepth = 1000 // Maximum depth of nested subcommands
+
+	for i := 0; i < maxCommandDepth && args.Len() > 0; i++ {
+		cmdName := args.CommandName()
+
+		cmd := cmds.Get(cmdName)
+		if cmd == nil {
+			break
+		}
+
+		args = args.Remove(cmdName)
+		cmds = cmd.Subcommands
+		ctx = ctx.NewCommandContext(cmd, args)
+	}
+
+	if ctx.Command != nil {
+		return cli.NewExitError(cli.ShowCommandHelp(ctx), cli.ExitCodeGeneralError)
+	}
+
+	return cli.NewExitError(errors.New(cli.InvalidCommandNameError(args.First())), cli.ExitCodeGeneralError)
+}
diff --git a/cli/commands/run/flags.go b/cli/commands/run/flags.go
index b8df8300e2..a7ea042102 100644
--- a/cli/commands/run/flags.go
+++ b/cli/commands/run/flags.go
@@ -371,7 +371,6 @@ func NewFlags(opts *options.TerragruntOptions, prefix flags.Prefix) cli.Flags {
 				Name:    terragruntPrefix.FlagName(DeprecatedIncludeModulePrefixFlagName),
 				EnvVars: terragruntPrefix.EnvVars(DeprecatedIncludeModulePrefixFlagName),
 				Usage:   "When this flag is set output from Terraform sub-commands is prefixed with module path.",
-				Hidden:  true,
 				Action: func(_ *cli.Context, _ bool) error {
 					opts.Logger.Warnf("The %q flag is deprecated. Use the functionality-inverted %q flag instead. By default, Terraform/OpenTofu output is integrated into the Terragrunt log, which prepends additional data, such as timestamps and prefixes, to log entries.", DeprecatedIncludeModulePrefixFlagName, TFForwardStdoutFlagName)
 
diff --git a/cli/commands/version/command.go b/cli/commands/version/command.go
index 0a3c1a1923..4a4f4dd949 100644
--- a/cli/commands/version/command.go
+++ b/cli/commands/version/command.go
@@ -2,7 +2,6 @@
 package version
 
 import (
-	"github.com/gruntwork-io/terragrunt/cli/flags/global"
 	"github.com/gruntwork-io/terragrunt/internal/cli"
 	"github.com/gruntwork-io/terragrunt/options"
 )
@@ -17,7 +16,11 @@ func NewCommand(opts *options.TerragruntOptions) *cli.Command {
 		Usage:  "Show terragrunt version.",
 		Hidden: true,
 		Action: func(ctx *cli.Context) error {
-			return cli.NewExitError(global.VersionAction(ctx, opts), 0)
+			return cli.NewExitError(Action(ctx), 0)
 		},
 	}
 }
+
+func Action(ctx *cli.Context) error {
+	return cli.ShowVersion(ctx)
+}
diff --git a/cli/flags/deprecated_flag.go b/cli/flags/deprecated_flag.go
index 12ad6089f7..e58486dac6 100644
--- a/cli/flags/deprecated_flag.go
+++ b/cli/flags/deprecated_flag.go
@@ -73,9 +73,9 @@ func (flag *DeprecatedFlag) SetStrictControls(mainFlag *Flag, regControlsFn Regi
 	flagNameControl := controls.NewDeprecatedFlagName(flag, mainFlag, newValue)
 	envVarControl := controls.NewDeprecatedEnvVar(flag, mainFlag, newValue)
 
-	flag.controls = strict.Controls{flagNameControl, envVarControl}
-
-	regControlsFn(flagNameControl, envVarControl)
+	if ok := regControlsFn(flagNameControl, envVarControl); ok {
+		flag.controls = strict.Controls{flagNameControl, envVarControl}
+	}
 }
 
 // NewValueFunc represents a function that returns a new value for the current flag if a deprecated flag is called.
@@ -95,42 +95,46 @@ func NewValue(val string) NewValueFunc {
 }
 
 // RegisterStrictControlsFunc represents a callback func that registers the given controls in the `opts.StrictControls` stict control tree .
-type RegisterStrictControlsFunc func(flagNameControl, envVarControl strict.Control)
+type RegisterStrictControlsFunc func(flagNameControl, envVarControl strict.Control) bool
 
 // StrictControlsByCommand returns a callback function that adds the taken controls as subcontrols for the given `controlNames`.
 // Using the given `commandName` as categories.
-func StrictControlsByCommand(strcitControls strict.Controls, commandName string, controlNames ...string) RegisterStrictControlsFunc {
-	return func(flagNameControl, envVarControl strict.Control) {
+func StrictControlsByCommand(strictControls strict.Controls, commandName string, controlNames ...string) RegisterStrictControlsFunc {
+	return func(flagNameControl, envVarControl strict.Control) bool {
 		flagNamesCategory := fmt.Sprintf(controls.CommandFlagsCategoryNameFmt, commandName)
 		envVarsCategory := fmt.Sprintf(controls.CommandEnvVarsCategoryNameFmt, commandName)
 
-		registerStrictControls(strcitControls, flagNameControl, envVarControl, flagNamesCategory, envVarsCategory, controlNames...)
+		return registerStrictControls(strictControls, flagNameControl, envVarControl, flagNamesCategory, envVarsCategory, controlNames...)
 	}
 }
 
 // StrictControlsByGlobalFlags returns a callback function that adds the taken controls as subcontrols for the given `controlNames`.
 // And assigns the "Global Flag" category to these controls.
-func StrictControlsByGlobalFlags(strcitControls strict.Controls, controlNames ...string) RegisterStrictControlsFunc {
-	return func(flagNameControl, envVarControl strict.Control) {
-		registerStrictControls(strcitControls, flagNameControl, envVarControl, controls.GlobalFlagsCategoryName, controls.GlobalEnvVarsCategoryName, controlNames...)
+func StrictControlsByGlobalFlags(strictControls strict.Controls, controlNames ...string) RegisterStrictControlsFunc {
+	return func(flagNameControl, envVarControl strict.Control) bool {
+		return registerStrictControls(strictControls, flagNameControl, envVarControl, controls.GlobalFlagsCategoryName, controls.GlobalEnvVarsCategoryName, controlNames...)
 	}
 }
 
 // StrictControlsByMovedGlobalFlags returns a callback function that adds the taken controls as subcontrols for the given `controlNames`.
 // And assigns the "Moved to other %s command global flags" category to these controls.
-func StrictControlsByMovedGlobalFlags(strcitControls strict.Controls, commandName string, controlNames ...string) RegisterStrictControlsFunc {
-	return func(flagNameControl, envVarControl strict.Control) {
+func StrictControlsByMovedGlobalFlags(strictControls strict.Controls, commandName string, controlNames ...string) RegisterStrictControlsFunc {
+	return func(flagNameControl, envVarControl strict.Control) bool {
 		flagNamesCategory := fmt.Sprintf(controls.MovedGlobalFlagsCategoryNameFmt, commandName)
-		registerStrictControls(strcitControls, flagNameControl, envVarControl, flagNamesCategory, "", controlNames...)
+		return registerStrictControls(strictControls, flagNameControl, envVarControl, flagNamesCategory, "", controlNames...)
 	}
 }
 
-func registerStrictControls(strcitControls strict.Controls,
+func registerStrictControls(strictControls strict.Controls,
 	flagNameControl, envVarControl strict.Control,
 	flagNamesCategory, envVarsCategory string,
-	controlNames ...string) {
+	controlNames ...string) bool {
+	if strictControls == nil {
+		return false
+	}
+
 	if flagNameControl != nil {
-		strcitControls.FilterByNames(append(
+		strictControls.FilterByNames(append(
 			controlNames,
 			controls.TerragruntPrefixFlags,
 			controls.DeprecatedFlags,
@@ -138,10 +142,12 @@ func registerStrictControls(strcitControls strict.Controls,
 	}
 
 	if envVarControl != nil {
-		strcitControls.FilterByNames(append(
+		strictControls.FilterByNames(append(
 			controlNames,
 			controls.TerragruntPrefixEnvVars,
 			controls.DeprecatedEnvVars,
 		)...).AddSubcontrolsToCategory(envVarsCategory, envVarControl)
 	}
+
+	return true
 }
diff --git a/cli/flags/flag.go b/cli/flags/flag.go
index db96126119..23bfb2bc59 100644
--- a/cli/flags/flag.go
+++ b/cli/flags/flag.go
@@ -33,37 +33,42 @@ func NewMovedFlag(deprecatedFlag cli.Flag, newCommandName string, regControlsFn
 	return NewFlag(nil, WithDeprecatedMovedFlag(deprecatedFlag, newCommandName, regControlsFn))
 }
 
-// TakesValue implements `cli.Flag` interface.
-func (new *Flag) TakesValue() bool {
-	if new.Flag.Value() == nil {
+// TakesValue implements `github.com/urfave/cli.DocGenerationFlag` required to generate help.
+// TakesValue returns `true` for all flags except boolean ones that are `false` or `true` inverted.
+func (newFlag *Flag) TakesValue() bool {
+	if newFlag.Flag.Value() == nil {
 		return false
 	}
 
-	val, ok := new.Flag.Value().Get().(bool)
+	val, ok := newFlag.Flag.Value().Get().(bool)
+
+	if newFlag.Flag.Value().IsNegativeBoolFlag() {
+		val = !val
+	}
 
 	return !ok || !val
 }
 
 // Value implements `cli.Flag` interface.
-func (new *Flag) Value() cli.FlagValue {
-	for _, deprecatedFlag := range new.deprecatedFlags {
+func (newFlag *Flag) Value() cli.FlagValue {
+	for _, deprecatedFlag := range newFlag.deprecatedFlags {
 		if deprecatedFlagValue := deprecatedFlag.Value(); deprecatedFlagValue.IsSet() && deprecatedFlag.newValueFn != nil {
 			newValue := deprecatedFlag.newValueFn(deprecatedFlagValue)
-			new.Flag.Value().Set(newValue) //nolint:errcheck
+			newFlag.Flag.Value().Getter(deprecatedFlagValue.GetName()).Set(newValue) //nolint:errcheck
 		}
 	}
 
-	return new.Flag.Value()
+	return newFlag.Flag.Value()
 }
 
 // Apply implements `cli.Flag` interface.
-func (new *Flag) Apply(set *flag.FlagSet) error {
-	if err := new.Flag.Apply(set); err != nil {
+func (newFlag *Flag) Apply(set *flag.FlagSet) error {
+	if err := newFlag.Flag.Apply(set); err != nil {
 		return err
 	}
 
-	for _, deprecated := range new.deprecatedFlags {
-		if deprecated.Flag == new.Flag {
+	for _, deprecated := range newFlag.deprecatedFlags {
+		if deprecated.Flag == newFlag.Flag {
 			if err := cli.ApplyFlag(deprecated, set); err != nil {
 				return err
 			}
@@ -80,13 +85,13 @@ func (new *Flag) Apply(set *flag.FlagSet) error {
 }
 
 // RunAction implements `cli.Flag` interface.
-func (new *Flag) RunAction(ctx *cli.Context) error {
-	for _, deprecated := range new.deprecatedFlags {
+func (newFlag *Flag) RunAction(ctx *cli.Context) error {
+	for _, deprecated := range newFlag.deprecatedFlags {
 		if err := deprecated.Evaluate(ctx); err != nil {
 			return err
 		}
 
-		if deprecated.Flag == nil || deprecated.Flag == new.Flag || !deprecated.Value().IsSet() {
+		if deprecated.Flag == nil || deprecated.Flag == newFlag.Flag || !deprecated.Value().IsSet() {
 			continue
 		}
 
@@ -95,7 +100,7 @@ func (new *Flag) RunAction(ctx *cli.Context) error {
 		}
 	}
 
-	if deprecated, ok := new.Flag.(interface {
+	if deprecated, ok := newFlag.Flag.(interface {
 		Evaluate(ctx context.Context) error
 	}); ok {
 		if err := deprecated.Evaluate(ctx); err != nil {
@@ -103,5 +108,12 @@ func (new *Flag) RunAction(ctx *cli.Context) error {
 		}
 	}
 
-	return new.Flag.RunAction(ctx)
+	return newFlag.Flag.RunAction(ctx)
+}
+
+// ParseEnvVars parses env vars values specified in the flag.
+// The value will be assigned to the `Destination` field.
+// The value can also be retrieved using `flag.Value().Get()`.
+func (newFlag *Flag) ParseEnvVars() error {
+	return newFlag.Apply(new(flag.FlagSet))
 }
diff --git a/cli/flags/flag_opts.go b/cli/flags/flag_opts.go
index 87c73084a6..3f0032595e 100644
--- a/cli/flags/flag_opts.go
+++ b/cli/flags/flag_opts.go
@@ -3,17 +3,27 @@ package flags
 import (
 	"fmt"
 
+	"slices"
+
 	"github.com/gruntwork-io/terragrunt/internal/cli"
 	"github.com/gruntwork-io/terragrunt/internal/strict"
 	"github.com/gruntwork-io/terragrunt/internal/strict/controls"
-	"golang.org/x/exp/slices"
 )
 
 // Option is used to set options to the `Flag`.
 type Option func(*Flag)
 
+// WithDeprecatedMovedFlag returns an `Option` that will register the given `deprecatedFlag`
+// as a deprecated flag that is moved to the given `commandName`. For example:
+//
+//	NewFlag(nil, WithDeprecatedMovedFlag(&cli.BoolFlag{
+//	  Name:    "no-auto-init",
+//	}, "run", nil))
+//
+// Log output:
+// WARN The `--no-auto-init` global flag is moved to `run` command and will be removed from the global flags in a future version. Use `run --no-auto-init` instead.
 func WithDeprecatedMovedFlag(deprecatedFlag cli.Flag, commandName string, regControlsFn RegisterStrictControlsFunc) Option {
-	return func(new *Flag) {
+	return func(newFlag *Flag) {
 		deprecatedNames := deprecatedFlag.Names()
 
 		if flag, ok := deprecatedFlag.(*Flag); ok {
@@ -28,22 +38,26 @@ func WithDeprecatedMovedFlag(deprecatedFlag cli.Flag, commandName string, regCon
 		}
 		slices.Reverse(deprecatedFlag.names)
 
-		newFlag := &DeprecatedFlag{
+		flag := &DeprecatedFlag{
 			Flag:  deprecatedFlag,
 			names: slices.Clone(deprecatedNames),
 		}
 
-		for i, name := range newFlag.names {
-			newFlag.names[i] = fmt.Sprintf("%s --%s", commandName, name)
+		for i, name := range flag.names {
+			flag.names[i] = fmt.Sprintf("%s --%s", commandName, name)
 		}
 
-		new.Flag = newFlag
+		newFlag.Flag = flag
 
-		flagNameControl := controls.NewDeprecatedMovedFlagName(deprecatedFlag, new, commandName)
+		flagNameControl := controls.NewDeprecatedMovedFlagName(deprecatedFlag, newFlag, commandName)
 
-		newFlag.controls = strict.Controls{flagNameControl}
+		if regControlsFn == nil {
+			return
+		}
 
-		regControlsFn(flagNameControl, nil)
+		if ok := regControlsFn(flagNameControl, nil); ok {
+			flag.controls = strict.Controls{flagNameControl}
+		}
 	}
 }
 
@@ -56,15 +70,15 @@ func WithDeprecatedMovedFlag(deprecatedFlag cli.Flag, commandName string, regCon
 //	  Name:    "terragrunt-json-log",
 //	}, flags.NewValue("json"), nil))
 func WithDeprecatedFlag(deprecatedFlag cli.Flag, newValueFn NewValueFunc, regControlsFn RegisterStrictControlsFunc) Option {
-	return func(new *Flag) {
+	return func(newFlag *Flag) {
 		deprecatedFlag := &DeprecatedFlag{
 			Flag:                   deprecatedFlag,
 			newValueFn:             newValueFn,
 			allowedSubcommandScope: true,
 		}
-		deprecatedFlag.SetStrictControls(new, regControlsFn)
+		deprecatedFlag.SetStrictControls(newFlag, regControlsFn)
 
-		new.deprecatedFlags = append(new.deprecatedFlags, deprecatedFlag)
+		newFlag.deprecatedFlags = append(newFlag.deprecatedFlags, deprecatedFlag)
 	}
 }
 
@@ -80,18 +94,18 @@ func WithDeprecatedFlag(deprecatedFlag cli.Flag, newValueFn NewValueFunc, regCon
 //	}, WithDeprecatedPrefix(Prefix{"terragrunt"}, nil))
 //
 // The deprecated flag will have "terragrunt-no-color","terragrunt-disable-color" names and "TERRAGRUNT_NO_COLOR","TERRAGRUNT_DISABLE_COLOR" env vars.
-// TODO: This function is currently unused but retained for future flag deprecation needs
+// NOTE: This function is currently unused but retained for future flag deprecation needs.
 func WithDeprecatedPrefix(prefix Prefix, regControlsFn RegisterStrictControlsFunc) Option {
-	return func(new *Flag) {
+	return func(newFlag *Flag) {
 		deprecatedFlag := &DeprecatedFlag{
-			Flag:                   new.Flag,
-			names:                  prefix.FlagNames(new.Flag.Names()...),
-			envVars:                prefix.EnvVars(new.Flag.Names()...),
+			Flag:                   newFlag.Flag,
+			names:                  prefix.FlagNames(newFlag.Flag.Names()...),
+			envVars:                prefix.EnvVars(newFlag.Flag.Names()...),
 			allowedSubcommandScope: true,
 		}
-		deprecatedFlag.SetStrictControls(new, regControlsFn)
+		deprecatedFlag.SetStrictControls(newFlag, regControlsFn)
 
-		new.deprecatedFlags = append(new.deprecatedFlags, deprecatedFlag)
+		newFlag.deprecatedFlags = append(newFlag.deprecatedFlags, deprecatedFlag)
 	}
 }
 
@@ -103,38 +117,38 @@ func WithDeprecatedPrefix(prefix Prefix, regControlsFn RegisterStrictControlsFun
 //
 // The deprecated flag will have "no-color","working-dir" names and "NO_COLOR","WORKING_DIR" env vars.
 func WithDeprecatedNames(flagNames []string, regControlsFn RegisterStrictControlsFunc) Option {
-	return func(new *Flag) {
+	return func(newFlag *Flag) {
 		deprecatedFlag := &DeprecatedFlag{
-			Flag:                   new.Flag,
+			Flag:                   newFlag.Flag,
 			names:                  Prefix{}.FlagNames(flagNames...),
 			envVars:                Prefix{}.EnvVars(flagNames...),
 			allowedSubcommandScope: true,
 		}
-		deprecatedFlag.SetStrictControls(new, regControlsFn)
+		deprecatedFlag.SetStrictControls(newFlag, regControlsFn)
 
-		new.deprecatedFlags = append(new.deprecatedFlags, deprecatedFlag)
+		newFlag.deprecatedFlags = append(newFlag.deprecatedFlags, deprecatedFlag)
 	}
 }
 
 // WithDeprecatedName does the same as `WithDeprecatedNames`, but with a single name.
 func WithDeprecatedName(flagName string, regControlsFn RegisterStrictControlsFunc) Option {
-	return func(new *Flag) {
-		WithDeprecatedNames([]string{flagName}, regControlsFn)(new)
+	return func(newFlag *Flag) {
+		WithDeprecatedNames([]string{flagName}, regControlsFn)(newFlag)
 	}
 }
 
 // WithDeprecatedNamesEnvVars returns an `Option` that will create a deprecated flag,
 // with the given `flagNames`, `envVars` assigned to the flag names and environment variables as is.
 func WithDeprecatedNamesEnvVars(flagNames, envVars []string, regControlsFn RegisterStrictControlsFunc) Option {
-	return func(new *Flag) {
+	return func(newFlag *Flag) {
 		deprecatedFlag := &DeprecatedFlag{
-			Flag:                   new.Flag,
+			Flag:                   newFlag.Flag,
 			names:                  flagNames,
 			envVars:                envVars,
 			allowedSubcommandScope: true,
 		}
-		deprecatedFlag.SetStrictControls(new, regControlsFn)
+		deprecatedFlag.SetStrictControls(newFlag, regControlsFn)
 
-		new.deprecatedFlags = append(new.deprecatedFlags, deprecatedFlag)
+		newFlag.deprecatedFlags = append(newFlag.deprecatedFlags, deprecatedFlag)
 	}
 }
diff --git a/cli/flags/global/flags.go b/cli/flags/global/flags.go
index c8cf4bf0b4..d1c1e1286a 100644
--- a/cli/flags/global/flags.go
+++ b/cli/flags/global/flags.go
@@ -3,17 +3,21 @@ package global
 
 import (
 	"fmt"
-	"os"
+
+	"slices"
 
 	"github.com/gruntwork-io/go-commons/collections"
+	"github.com/gruntwork-io/terragrunt/cli/commands"
+	"github.com/gruntwork-io/terragrunt/cli/commands/help"
+	"github.com/gruntwork-io/terragrunt/cli/commands/version"
 	"github.com/gruntwork-io/terragrunt/cli/flags"
 	"github.com/gruntwork-io/terragrunt/internal/cli"
-	"github.com/gruntwork-io/terragrunt/internal/errors"
 	"github.com/gruntwork-io/terragrunt/internal/strict"
 	"github.com/gruntwork-io/terragrunt/internal/strict/controls"
 	"github.com/gruntwork-io/terragrunt/options"
 	"github.com/gruntwork-io/terragrunt/pkg/log"
 	"github.com/gruntwork-io/terragrunt/pkg/log/format"
+	"github.com/gruntwork-io/terragrunt/util"
 )
 
 const (
@@ -66,6 +70,39 @@ const (
 	DeprecatedTfLogJSONFlagName            = "tf-logs-to-json"
 )
 
+// NewFlagsWithDeprecatedMovedFlags returns global flags along with flags that have been moved to other commands and hidden from CLI help.
+func NewFlagsWithDeprecatedMovedFlags(opts *options.TerragruntOptions) cli.Flags {
+	globalFlags := NewFlags(opts, nil)
+
+	// Since the flags we take from the command may already have deprecated flags,
+	// and we create new strict controls each time we call `commands.New` which will then be evaluated,
+	// we need to clear `Strict Controls` to avoid them being displayed and causing duplicate warnings, for example, log output:
+	//
+	// WARN The global `--terragrunt-no-auto-init` flag has moved to the `run` command and will not be accessible as a global flag in a future version of Terragrunt. Use `run --no-auto-init` instead.
+	// WARN The `--terragrunt-no-auto-init` flag is deprecated and will be removed in a future version of Terragrunt. Use `--no-auto-init` instead.
+	cmdOpts := opts.Clone()
+	cmdOpts.StrictControls = nil
+
+	commands := commands.New(cmdOpts)
+
+	var seen []string
+
+	for _, cmd := range commands {
+		for _, flag := range cmd.Flags {
+			flagName := util.FirstElement(util.RemoveEmptyElements(flag.Names()))
+
+			if slices.Contains(seen, flagName) {
+				continue
+			}
+
+			seen = append(seen, flagName)
+			globalFlags = append(globalFlags, flags.NewMovedFlag(flag, cmd.Name, flags.StrictControlsByMovedGlobalFlags(opts.StrictControls, cmd.Name)))
+		}
+	}
+
+	return globalFlags
+}
+
 // NewFlags creates and returns global flags common for all commands.
 func NewFlags(opts *options.TerragruntOptions, prefix flags.Prefix) cli.Flags {
 	tgPrefix := prefix.Prepend(flags.TgPrefix)
@@ -234,7 +271,7 @@ func NewFlags(opts *options.TerragruntOptions, prefix flags.Prefix) cli.Flags {
 	return flags
 }
 
-func NewLogLevelFlag(opts *options.TerragruntOptions, prefix flags.Prefix) cli.Flag {
+func NewLogLevelFlag(opts *options.TerragruntOptions, prefix flags.Prefix) *flags.Flag {
 	tgPrefix := prefix.Prepend(flags.TgPrefix)
 	terragruntPrefix := prefix.Prepend(flags.TerragruntPrefix)
 	terragruntPrefixControl := flags.StrictControlsByGlobalFlags(opts.StrictControls)
@@ -270,7 +307,7 @@ func NewHelpVersionFlags(opts *options.TerragruntOptions) cli.Flags {
 			Aliases: []string{"h"}, //  -h
 			Usage:   "Show help.",
 			Action: func(ctx *cli.Context, _ bool) error {
-				return HelpAction(ctx, opts)
+				return help.Action(ctx, opts)
 			},
 		},
 		&cli.BoolFlag{
@@ -278,51 +315,8 @@ func NewHelpVersionFlags(opts *options.TerragruntOptions) cli.Flags {
 			Aliases: []string{"v"},   //  -v
 			Usage:   "Show terragrunt version.",
 			Action: func(ctx *cli.Context, _ bool) (err error) {
-				return VersionAction(ctx, opts)
+				return version.Action(ctx)
 			},
 		},
 	}
 }
-
-func HelpAction(ctx *cli.Context, opts *options.TerragruntOptions) error {
-	var (
-		args = ctx.Args()
-		cmds = ctx.App.Commands
-	)
-
-	if opts.Logger.Level() >= log.DebugLevel {
-		// https: //github.com/urfave/cli/blob/f035ffaa3749afda2cd26fb824aa940747297ef1/help.go#L401
-		if err := os.Setenv("CLI_TEMPLATE_ERROR_DEBUG", "1"); err != nil {
-			return errors.Errorf("failed to set CLI_TEMPLATE_ERROR_DEBUG environment variable: %w", err)
-		}
-	}
-
-	if args.CommandName() == "" {
-		return cli.ShowAppHelp(ctx)
-	}
-
-	const maxIterations = 1000
-
-	for range maxIterations {
-		cmdName := args.CommandName()
-
-		cmd := cmds.Get(cmdName)
-		if cmd == nil {
-			break
-		}
-
-		args = args.Remove(cmdName)
-		cmds = cmd.Subcommands
-		ctx = ctx.NewCommandContext(cmd, args)
-	}
-
-	if ctx.Command != nil {
-		return cli.NewExitError(cli.ShowCommandHelp(ctx), cli.ExitCodeGeneralError)
-	}
-
-	return cli.NewExitError(errors.New(cli.InvalidCommandNameError(args.First())), cli.ExitCodeGeneralError)
-}
-
-func VersionAction(ctx *cli.Context, _ *options.TerragruntOptions) error {
-	return cli.ShowVersion(ctx)
-}
diff --git a/internal/cli/bool_flag.go b/internal/cli/bool_flag.go
index 9565260225..fc08133e6f 100644
--- a/internal/cli/bool_flag.go
+++ b/internal/cli/bool_flag.go
@@ -31,7 +31,8 @@ type BoolFlag struct {
 	// Destination ia a pointer to which the value of the flag or env var is assigned.
 	// It also uses as the default value displayed in the help.
 	Destination *bool
-	// If set to true, then the assigned flag value will be inverted
+	// If set to true, then the assigned flag value will be inverted.
+	// Example: With `Negative: true`, `--boolean-flag` sets the value to `false`, and `--boolean-flag=false` sets the value to `true`.
 	Negative bool
 	// Hidden hides the flag from the help, if set to true.
 	Hidden bool
@@ -49,6 +50,7 @@ func (flag *BoolFlag) Apply(set *libflag.FlagSet) error {
 	flag.FlagValue = &flagValue{
 		value:            value,
 		initialTextValue: value.String(),
+		negative:         flag.Negative,
 	}
 
 	return ApplyFlag(flag, set)
diff --git a/internal/cli/flag.go b/internal/cli/flag.go
index 5857f4d554..ae087a3c3b 100644
--- a/internal/cli/flag.go
+++ b/internal/cli/flag.go
@@ -62,10 +62,13 @@ type FlagValue interface {
 	// IsEnvSet returns true if the flag was set by env var.
 	IsEnvSet() bool
 
-	// optional interface to indicate boolean flags that can be
-	// supplied without "=value" text
+	// IsBoolFlag returns true if the flag is of type bool.
 	IsBoolFlag() bool
 
+	// IsNegativeBoolFlag returns true if the boolean flag's value should be inverted.
+	// Example: For a flag with Negative=true, when set to true it returns false, and vice versa.
+	IsNegativeBoolFlag() bool
+
 	// MultipleSet returns true if the flag allows multiple assignments, such as slice/map.
 	MultipleSet() bool
 }
@@ -145,17 +148,24 @@ type flagValue struct {
 	hasBeenSet       bool
 	envHasBeenSet    bool
 	initialTextValue string
+	negative         bool
 }
 
 func (flag *flagValue) MultipleSet() bool {
 	return flag.multipleSet
 }
 
+// IsBoolFlag implements `cli.FlagValue` interface.
 func (flag *flagValue) IsBoolFlag() bool {
 	_, ok := flag.value.Get().(bool)
 	return ok
 }
 
+// IsNegativeBoolFlag implements `cli.FlagValue` interface.
+func (flag *flagValue) IsNegativeBoolFlag() bool {
+	return flag.negative
+}
+
 func (flag *flagValue) Get() any {
 	return flag.value.Get()
 }
@@ -220,7 +230,7 @@ func (flag *flag) Value() FlagValue {
 	return flag.FlagValue
 }
 
-// TakesValue returns true of the flag takes a value, otherwise false.
+// TakesValue returns true if the flag needs to be given a value.
 // Implements `cli.DocGenerationFlag.TakesValue` required to generate help.
 func (flag *flag) TakesValue() bool {
 	return true
diff --git a/internal/cli/help.go b/internal/cli/help.go
index a6419fb3b0..e521de3ddf 100644
--- a/internal/cli/help.go
+++ b/internal/cli/help.go
@@ -3,9 +3,10 @@ package cli
 import (
 	"strings"
 
+	"slices"
+
 	"github.com/gruntwork-io/terragrunt/internal/errors"
 	"github.com/urfave/cli/v2"
-	"golang.org/x/exp/slices"
 )
 
 var (
diff --git a/internal/experiment/experiment.go b/internal/experiment/experiment.go
index d95a3dfc89..0a0ac56e7e 100644
--- a/internal/experiment/experiment.go
+++ b/internal/experiment/experiment.go
@@ -8,8 +8,9 @@
 package experiment
 
 import (
+	"slices"
+
 	"github.com/gruntwork-io/terragrunt/pkg/log"
-	"golang.org/x/exp/slices"
 )
 
 const (
diff --git a/internal/strict/category.go b/internal/strict/category.go
index bb0ea8c356..e0388a8fe0 100644
--- a/internal/strict/category.go
+++ b/internal/strict/category.go
@@ -3,7 +3,7 @@ package strict
 import (
 	"sort"
 
-	"golang.org/x/exp/slices"
+	"slices"
 )
 
 // Categories is multiple of DeprecatedFlag Category.
diff --git a/internal/strict/control.go b/internal/strict/control.go
index 8f86dffcf5..f54f95e923 100644
--- a/internal/strict/control.go
+++ b/internal/strict/control.go
@@ -5,8 +5,9 @@ import (
 	"sort"
 	"strings"
 
+	"slices"
+
 	"github.com/gruntwork-io/terragrunt/pkg/log"
-	"golang.org/x/exp/slices"
 )
 
 const CompletedControlsFmt = "The following strict control(s) are already completed: %s. Please remove any completed strict controls, as setting them no longer does anything. For a list of all ongoing strict controls, and the outcomes of previous strict controls, see https://terragrunt.gruntwork.io/docs/reference/strict-mode or get the actual list by running the `terragrunt info strict` command."
diff --git a/internal/strict/controls/deprecated_command.go b/internal/strict/controls/deprecated_command.go
index c00990ac06..5f73c3af2d 100644
--- a/internal/strict/controls/deprecated_command.go
+++ b/internal/strict/controls/deprecated_command.go
@@ -17,6 +17,6 @@ func NewDeprecatedCommand(command, newCommand string) *Control {
 		Name:        command,
 		Description: "replaced with: " + newCommand,
 		Error:       errors.Errorf("The `%s` command is no longer supported. Use `%s` instead.", command, newCommand),
-		Warning:     fmt.Sprintf("The `%s` command is deprecated and will be removed in a future version. Use `%s` instead.", command, newCommand),
+		Warning:     fmt.Sprintf("The `%s` command is deprecated and will be removed in a future version of Terragrunt. Use `%s` instead.", command, newCommand),
 	}
 }
diff --git a/internal/strict/controls/deprecated_env_var.go b/internal/strict/controls/deprecated_env_var.go
index eb17ec5dbd..6547387c7f 100644
--- a/internal/strict/controls/deprecated_env_var.go
+++ b/internal/strict/controls/deprecated_env_var.go
@@ -3,12 +3,13 @@ package controls
 import (
 	"context"
 
+	"slices"
+
 	"github.com/gruntwork-io/terragrunt/internal/cli"
 	"github.com/gruntwork-io/terragrunt/internal/errors"
 	"github.com/gruntwork-io/terragrunt/internal/strict"
 	"github.com/gruntwork-io/terragrunt/pkg/log"
 	"github.com/gruntwork-io/terragrunt/util"
-	"golang.org/x/exp/slices"
 )
 
 const (
@@ -46,9 +47,9 @@ func NewDeprecatedEnvVar(deprecatedFlag, newFlag cli.Flag, newValue string) *Dep
 			Name:        deprecatedName,
 			Description: "replaced with: " + newName,
 		},
-		ErrorFmt:   "The `%s` env var is no longer supported. Use `%s` instead.",
-		WarningFmt: "The `%s` env var is deprecated and will be removed in a future version. Use `%s` instead.",
-
+		ErrorFmt: "The `%s` environment variable is no longer supported. Use `%s` instead.",
+		// The `TERRAGRUNT_LOG_LEVEL` environment variable is deprecated and will be removed in a future version of Terragrunt. Use `TG_LOG_LEVEL=trace` instead.
+		WarningFmt:     "The `%s` environment variable is deprecated and will be removed in a future version of Terragrunt. Use `%s` instead.",
 		deprecatedFlag: deprecatedFlag,
 		newFlag:        newFlag,
 	}
@@ -61,7 +62,7 @@ func (ctrl *DeprecatedEnvVar) Evaluate(ctx context.Context) error {
 		envName   string
 	)
 
-	if valueName == "" || !ctrl.deprecatedFlag.Value().IsEnvSet() || slices.Contains(ctrl.newFlag.GetEnvVars(), valueName) {
+	if valueName == "" || !ctrl.deprecatedFlag.Value().IsEnvSet() || !slices.Contains(ctrl.deprecatedFlag.GetEnvVars(), valueName) {
 		return nil
 	}
 
diff --git a/internal/strict/controls/deprecated_flag_name.go b/internal/strict/controls/deprecated_flag_name.go
index d638e38159..4390c81926 100644
--- a/internal/strict/controls/deprecated_flag_name.go
+++ b/internal/strict/controls/deprecated_flag_name.go
@@ -3,12 +3,13 @@ package controls
 import (
 	"context"
 
+	"slices"
+
 	"github.com/gruntwork-io/terragrunt/internal/cli"
 	"github.com/gruntwork-io/terragrunt/internal/errors"
 	"github.com/gruntwork-io/terragrunt/internal/strict"
 	"github.com/gruntwork-io/terragrunt/pkg/log"
 	"github.com/gruntwork-io/terragrunt/util"
-	"golang.org/x/exp/slices"
 )
 
 const (
@@ -40,8 +41,10 @@ func NewDeprecatedMovedFlagName(deprecatedFlag, newFlag cli.Flag, commandName st
 			Name:        deprecatedName,
 			Description: "replaced with: " + newName,
 		},
-		ErrorFmt:       "The `--%s` global flag is no longer supported. Use `--%s` instead.",
-		WarningFmt:     "The `--%s` global flag is moved to `" + commandName + "` command and will be removed from the global flags in a future version. Use `--%s` instead.",
+		ErrorFmt: "The global `--%s` flag has moved to the `" + commandName + "` command and is no longer supported as a global flag. Use `%s` instead.",
+		// The second argument to fmt comes with the command name. Output example:
+		// The global `--terragrunt-no-auto-init` flag has moved to the `run` command and will not be accessible as a global flag in a future version of Terragrunt. Use `run --no-auto-init` instead.
+		WarningFmt:     "The global `--%s` flag has moved to the `" + commandName + "` command and will not be accessible as a global flag in a future version of Terragrunt. Use `%s` instead.",
 		deprecatedFlag: deprecatedFlag,
 		newFlag:        newFlag,
 	}
@@ -65,8 +68,10 @@ func NewDeprecatedFlagName(deprecatedFlag, newFlag cli.Flag, newValue string) *D
 			Name:        deprecatedName,
 			Description: "replaced with: " + newName,
 		},
-		ErrorFmt:       "The `--%s` flag is no longer supported. Use `--%s` instead.",
-		WarningFmt:     "The `--%s` flag is deprecated and will be removed in a future version. Use `--%s` instead.",
+		ErrorFmt: "The `--%s` flag is no longer supported. Use `--%s` instead.",
+		// Output example:
+		// The `--terragrunt-working-dir` flag is deprecated and will be removed in a future version of Terragrunt. Use `--working-dir=./test/fixtures/extra-args/` instead.
+		WarningFmt:     "The `--%s` flag is deprecated and will be removed in a future version of Terragrunt. Use `--%s` instead.",
 		deprecatedFlag: deprecatedFlag,
 		newFlag:        newFlag,
 	}
@@ -79,7 +84,7 @@ func (ctrl *DeprecatedFlagName) Evaluate(ctx context.Context) error {
 		flagName  string
 	)
 
-	if valueName == "" || !ctrl.deprecatedFlag.Value().IsArgSet() || slices.Contains(ctrl.newFlag.Names(), valueName) {
+	if valueName == "" || !ctrl.deprecatedFlag.Value().IsArgSet() || !slices.Contains(ctrl.deprecatedFlag.Names(), valueName) {
 		return nil
 	}
 
diff --git a/internal/strict/status.go b/internal/strict/status.go
index a1a0e9e907..8264d27353 100644
--- a/internal/strict/status.go
+++ b/internal/strict/status.go
@@ -1,6 +1,6 @@
 package strict
 
-import "golang.org/x/exp/slices"
+import "slices"
 
 const (
 	// ActiveStatus is the Status of a control that is ongoing.
diff --git a/main.go b/main.go
index aecccec3e5..d2ca7e9556 100644
--- a/main.go
+++ b/main.go
@@ -2,7 +2,6 @@ package main
 
 import (
 	"context"
-	"flag"
 	"os"
 
 	"github.com/gruntwork-io/terragrunt/cli"
@@ -22,7 +21,7 @@ func main() {
 	opts := options.NewTerragruntOptions()
 
 	// Immediately parse the `TG_LOG_LEVEL` environment variable, e.g. to set the TRACE level.
-	if err := global.NewLogLevelFlag(opts, nil).Apply(new(flag.FlagSet)); err != nil {
+	if err := global.NewLogLevelFlag(opts, nil).ParseEnvVars(); err != nil {
 		opts.Logger.Error(err.Error())
 		os.Exit(1)
 	}
diff --git a/tf/getproviders/lock.go b/tf/getproviders/lock.go
index 445c9fd243..e425e290e3 100644
--- a/tf/getproviders/lock.go
+++ b/tf/getproviders/lock.go
@@ -12,6 +12,8 @@ import (
 	"strings"
 	"unicode"
 
+	"slices"
+
 	"github.com/gruntwork-io/terragrunt/internal/errors"
 	"github.com/gruntwork-io/terragrunt/tf"
 	"github.com/gruntwork-io/terragrunt/util"
@@ -19,7 +21,6 @@ import (
 	"github.com/hashicorp/hcl/v2/hclsyntax"
 	"github.com/hashicorp/hcl/v2/hclwrite"
 	"github.com/zclconf/go-cty/cty"
-	"golang.org/x/exp/slices"
 )
 
 // UpdateLockfile updates the dependency lock file. If `.terraform.lock.hcl` does not exist, it will be created, otherwise it will be updated.


Test patch:
diff --git a/cli/app_test.go b/cli/app_test.go
index 7c890d98c9..39100f33c5 100644
--- a/cli/app_test.go
+++ b/cli/app_test.go
@@ -503,7 +503,7 @@ func setCommandAction(action clipkg.ActionFunc, cmds ...*clipkg.Command) {
 func runAppTest(args []string, opts *options.TerragruntOptions) (*options.TerragruntOptions, error) {
 	emptyAction := func(ctx *clipkg.Context) error { return nil }
 
-	terragruntCommands := cli.TerragruntCommands(opts)
+	terragruntCommands := commands.New(opts)
 	setCommandAction(emptyAction, terragruntCommands...)
 
 	app := clipkg.NewApp()
diff --git a/cli/flags/flag_test.go b/cli/flags/flag_test.go
new file mode 100644
index 0000000000..f966f33a9f
--- /dev/null
+++ b/cli/flags/flag_test.go
@@ -0,0 +1,183 @@
+package flags_test
+
+import (
+	"bytes"
+	"context"
+	"flag"
+	"fmt"
+	"strings"
+	"testing"
+
+	"github.com/gruntwork-io/terragrunt/cli/flags"
+	"github.com/gruntwork-io/terragrunt/internal/cli"
+	"github.com/gruntwork-io/terragrunt/internal/strict"
+	"github.com/gruntwork-io/terragrunt/internal/strict/controls"
+	"github.com/gruntwork-io/terragrunt/pkg/log"
+	"github.com/gruntwork-io/terragrunt/pkg/log/format"
+	"github.com/gruntwork-io/terragrunt/pkg/log/format/placeholders"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func mockDestValue[T any](val T) *T {
+	return &val
+}
+
+func newLogger() (log.Logger, *bytes.Buffer) {
+	formatter := format.NewFormatter(placeholders.Placeholders{placeholders.Message()})
+	output := new(bytes.Buffer)
+	logger := log.New(log.WithOutput(output), log.WithLevel(log.InfoLevel), log.WithFormatter(formatter))
+
+	return logger, output
+}
+
+func TestFlag_TakesValue(t *testing.T) {
+	t.Parallel()
+
+	testCases := []struct {
+		flag     cli.Flag
+		expected bool
+	}{
+		{
+			&cli.BoolFlag{Name: "name", Destination: mockDestValue(false)},
+			true,
+		},
+		{
+			&cli.BoolFlag{Name: "name", Destination: mockDestValue(true)},
+			false,
+		},
+		{
+			&cli.BoolFlag{Name: "name", Negative: true, Destination: mockDestValue(true)},
+			true,
+		},
+		{
+			&cli.BoolFlag{Name: "name", Negative: true, Destination: mockDestValue(false)},
+			false,
+		},
+		{
+			&cli.GenericFlag[string]{Name: "name", Destination: mockDestValue("value")},
+			true,
+		},
+	}
+
+	for i, testCase := range testCases {
+		t.Run(fmt.Sprintf("testCase-%d", i), func(t *testing.T) {
+			t.Parallel()
+
+			testFlag := flags.NewFlag(testCase.flag)
+
+			err := testFlag.Apply(new(flag.FlagSet))
+			require.NoError(t, err)
+
+			assert.Equal(t, testCase.expected, testFlag.TakesValue())
+		})
+	}
+}
+
+func TestFlag_Evaluate(t *testing.T) {
+	t.Parallel()
+
+	mockRegControls := func(flagNameControl, envVarControl strict.Control) bool {
+		return true
+	}
+
+	deprecatedMovedFlagWarning := func(cmdName string) string {
+		return controls.NewDeprecatedMovedFlagName(&cli.BoolFlag{}, &cli.BoolFlag{}, cmdName).WarningFmt
+	}
+
+	deprecatedFlagWarning := func() string {
+		return controls.NewDeprecatedFlagName(&cli.BoolFlag{}, &cli.BoolFlag{}, "").WarningFmt
+	}
+
+	deprecatedEnvVarWarning := func() string {
+		return controls.NewDeprecatedEnvVar(&cli.BoolFlag{}, &cli.BoolFlag{}, "").WarningFmt
+	}
+
+	type testCaseFlag struct {
+		flag   *flags.Flag
+		arg    string
+		envVar string
+	}
+
+	testCases := []struct {
+		flags          []testCaseFlag
+		expectedOutput []string
+	}{
+		{
+			[]testCaseFlag{
+				{
+					flags.NewMovedFlag(&cli.BoolFlag{Name: "flag-name"}, "run", mockRegControls),
+					"flag-name",
+					"",
+				},
+				{
+					flags.NewMovedFlag(flags.NewFlag(
+						&cli.BoolFlag{Name: "new-flag-name"},
+						flags.WithDeprecatedName("old-flag-name", nil),
+					), "run", mockRegControls),
+					"old-flag-name",
+					"",
+				},
+			},
+			[]string{
+				fmt.Sprintf(deprecatedMovedFlagWarning("run"), "flag-name", "run --flag-name"),
+				fmt.Sprintf(deprecatedMovedFlagWarning("run"), "old-flag-name", "run --new-flag-name"),
+			},
+		},
+		{
+			[]testCaseFlag{
+				{
+					flags.NewFlag(
+						&cli.BoolFlag{Name: "new-flag-name"},
+						flags.WithDeprecatedName("old-flag-name", mockRegControls),
+					),
+					"old-flag-name",
+					"",
+				},
+				{
+					flags.NewFlag(
+						&cli.BoolFlag{Name: "new-env-var-name", EnvVars: []string{"NEW_ENV_VAR_NAME"}},
+						flags.WithDeprecatedName("old-env-var-name", mockRegControls),
+					),
+					"",
+					"OLD_ENV_VAR_NAME",
+				},
+			},
+			[]string{
+				fmt.Sprintf(deprecatedFlagWarning(), "old-flag-name", "new-flag-name"),
+				fmt.Sprintf(deprecatedEnvVarWarning(), "OLD_ENV_VAR_NAME", "NEW_ENV_VAR_NAME=true"),
+			},
+		},
+	}
+
+	for i, testCase := range testCases {
+		t.Run(fmt.Sprintf("testCase-%d", i), func(t *testing.T) {
+			t.Parallel()
+
+			logger, output := newLogger()
+			ctx := context.Background()
+			ctx = log.ContextWithLogger(ctx, logger)
+
+			for _, testFlag := range testCase.flags {
+				err := testFlag.flag.Apply(new(flag.FlagSet))
+				require.NoError(t, err)
+
+				if testFlag.arg != "" {
+					err := testFlag.flag.Value().Getter(testFlag.arg).Set("1")
+					require.NoError(t, err)
+				}
+
+				if testFlag.envVar != "" {
+					err := testFlag.flag.Value().Getter(testFlag.envVar).EnvSet("1")
+					require.NoError(t, err)
+				}
+
+				err = testFlag.flag.RunAction(cli.NewAppContext(ctx, nil, nil))
+				require.NoError(t, err)
+			}
+
+			outputLines := strings.Split(strings.TrimSpace(output.String()), "\n")
+			assert.Equal(t, testCase.expectedOutput, outputLines)
+		})
+	}
+}