Add build-time contract tests and restic flag completions

Amolith created

Change summary

cmd/completions.go              |  91 +++++
cmd/completions_test.go         | 128 ++++++++
cmd/contract_test.go            | 116 ++++++++
cmd/gen-restic-cmds/main.go     |   4 
cmd/root.go                     |  12 
cmd/wrapspec.go                 |  92 ++++++
internal/restic/command.go      |   1 
internal/restic/commands_gen.go | 502 +++++++++++++++++-----------------
8 files changed, 668 insertions(+), 278 deletions(-)

Detailed changes

cmd/completions.go 🔗

@@ -7,19 +7,9 @@ import (
 	"github.com/spf13/cobra"
 
 	"git.secluded.site/keld/internal/config"
+	"git.secluded.site/keld/internal/restic"
 )
 
-// knownCommands lists the restic subcommands keld knows about. This is the
-// same set shown in the interactive menu (minus "quit").
-var knownCommands = []string{
-	"backup",
-	"check",
-	"forget",
-	"init",
-	"restore",
-	"snapshots",
-}
-
 // keldFlags lists keld's own flags (the ones extracted in extractOwnFlags).
 // Cobra can't advertise these automatically because DisableFlagParsing is on.
 var keldFlags = []string{
@@ -48,8 +38,13 @@ func init() {
 //
 // When the current word starts with "-", keld's own flags are offered instead.
 func completeArgs(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
-	// When the user is typing a flag, offer keld's own flags.
+	// When the user is typing a flag, offer restic flags for the
+	// identified command (plus global flags), or keld's own flags if
+	// no command is known yet.
 	if len(toComplete) > 0 && toComplete[0] == '-' {
+		if cmd := commandFromArgs(args); cmd != "" {
+			return completeResticFlags(cmd, toComplete), cobra.ShellCompDirectiveNoFileComp
+		}
 		return keldFlags, cobra.ShellCompDirectiveNoFileComp
 	}
 
@@ -191,3 +186,75 @@ func hasEqualSign(arg string) bool {
 	}
 	return false
 }
+
+// commandFromArgs extracts the restic command name from the already-accepted
+// args. It finds the first or second positional token (depending on whether
+// the first is a preset or a command) and returns it if it exists in
+// restic.Commands.
+func commandFromArgs(args []string) string {
+	var positionals []string
+loop:
+	for i := 0; i < len(args); i++ {
+		arg := args[i]
+		switch {
+		case arg == "--":
+			return ""
+		case isFlagToken(arg):
+			if !hasEqualSign(arg) && i+1 < len(args) && !isFlagToken(args[i+1]) && args[i+1] != "--" {
+				i++
+			}
+		default:
+			positionals = append(positionals, arg)
+			if len(positionals) == 2 {
+				break loop
+			}
+		}
+	}
+
+	// With 1 positional, it could be a command.
+	// With 2 positionals, the second is the command.
+	for i := len(positionals) - 1; i >= 0; i-- {
+		if _, ok := restic.Commands[positionals[i]]; ok && positionals[i] != "global" {
+			return positionals[i]
+		}
+	}
+	return ""
+}
+
+// completeResticFlags returns flag completions for the given restic command,
+// including global flags. Flags are prefixed with "--" (long) and "-" (short
+// aliases). Results are filtered by the toComplete prefix.
+func completeResticFlags(command, toComplete string) []string {
+	seen := make(map[string]struct{})
+	var out []string
+
+	addOptions := func(opts []restic.Option) {
+		for _, opt := range opts {
+			long := "--" + opt.Name
+			if _, ok := seen[long]; !ok {
+				seen[long] = struct{}{}
+				if strings.HasPrefix(long, toComplete) {
+					out = append(out, long)
+				}
+			}
+			if opt.Alias != "" {
+				short := "-" + opt.Alias
+				if _, ok := seen[short]; !ok {
+					seen[short] = struct{}{}
+					if strings.HasPrefix(short, toComplete) {
+						out = append(out, short)
+					}
+				}
+			}
+		}
+	}
+
+	if cmd, ok := restic.Commands[command]; ok {
+		addOptions(cmd.Options)
+	}
+	if global, ok := restic.Commands["global"]; ok {
+		addOptions(global.Options)
+	}
+
+	return out
+}

cmd/completions_test.go 🔗

@@ -97,8 +97,11 @@ func TestCompleteArgsAfterPreset(t *testing.T) {
 		t.Fatalf("expected NoFileComp, got %v", directive)
 	}
 	sort.Strings(completions)
-	if !reflect.DeepEqual(completions, knownCommands) {
-		t.Fatalf("after preset, expected commands %v, got %v", knownCommands, completions)
+	want := make([]string, len(knownCommands))
+	copy(want, knownCommands)
+	sort.Strings(want)
+	if !reflect.DeepEqual(completions, want) {
+		t.Fatalf("after preset, expected commands %v, got %v", want, completions)
 	}
 }
 
@@ -247,6 +250,127 @@ func TestIsKnownCommand(t *testing.T) {
 	}
 }
 
+func TestCompleteArgsResticFlagsAfterCommand(t *testing.T) {
+	t.Parallel()
+
+	tests := []struct {
+		name       string
+		args       []string
+		toComplete string
+		wantFlags  []string // flags that must appear in completions
+		wantAbsent []string // flags that must not appear
+	}{
+		{
+			name:       "backup flags with -- prefix",
+			args:       []string{"backup"},
+			toComplete: "--",
+			wantFlags:  []string{"--dry-run", "--exclude", "--tag"},
+			wantAbsent: []string{"--target"}, // restore-only flag
+		},
+		{
+			name:       "backup short aliases",
+			args:       []string{"backup"},
+			toComplete: "-",
+			wantFlags:  []string{"-n", "-e", "--dry-run", "--repo"},
+		},
+		{
+			name:       "restore flags include target",
+			args:       []string{"restore"},
+			toComplete: "--t",
+			wantFlags:  []string{"--target", "--tag", "--tls-client-cert"},
+		},
+		{
+			name:       "global flags included with command",
+			args:       []string{"backup"},
+			toComplete: "--re",
+			wantFlags:  []string{"--repo", "--repository-file", "--retry-lock"},
+		},
+		{
+			name:       "preset then command",
+			args:       []string{"home@cloud", "backup"},
+			toComplete: "--dr",
+			wantFlags:  []string{"--dry-run"},
+		},
+		{
+			name:       "flag then command",
+			args:       []string{"--config", "./keld.toml", "backup"},
+			toComplete: "--",
+			wantFlags:  []string{"--dry-run", "--exclude"},
+		},
+	}
+
+	for _, tt := range tests {
+		tt := tt
+		t.Run(tt.name, func(t *testing.T) {
+			t.Parallel()
+
+			completions, directive := completeArgs(nil, tt.args, tt.toComplete)
+			if directive != cobra.ShellCompDirectiveNoFileComp {
+				t.Fatalf("expected NoFileComp, got %v", directive)
+			}
+
+			has := make(map[string]bool, len(completions))
+			for _, c := range completions {
+				has[c] = true
+			}
+
+			for _, want := range tt.wantFlags {
+				if !has[want] {
+					t.Errorf("missing expected flag %q in completions %v", want, completions)
+				}
+			}
+			for _, absent := range tt.wantAbsent {
+				if has[absent] {
+					t.Errorf("unexpected flag %q in completions %v", absent, completions)
+				}
+			}
+		})
+	}
+}
+
+func TestCompleteArgsNoCommandStillReturnsKeldFlags(t *testing.T) {
+	t.Parallel()
+
+	completions, directive := completeArgs(nil, nil, "--")
+	if directive != cobra.ShellCompDirectiveNoFileComp {
+		t.Fatalf("expected NoFileComp, got %v", directive)
+	}
+	sort.Strings(completions)
+	want := []string{"--config", "--dry-run", "--help"}
+	if !reflect.DeepEqual(completions, want) {
+		t.Fatalf("without command, flag completions mismatch: got %v, want %v", completions, want)
+	}
+}
+
+func TestCommandFromArgs(t *testing.T) {
+	t.Parallel()
+
+	tests := []struct {
+		name string
+		args []string
+		want string
+	}{
+		{name: "empty", args: nil, want: ""},
+		{name: "command only", args: []string{"backup"}, want: "backup"},
+		{name: "preset then command", args: []string{"home@cloud", "backup"}, want: "backup"},
+		{name: "unknown preset alone", args: []string{"home@cloud"}, want: ""},
+		{name: "flag then command", args: []string{"--config", "./f", "backup"}, want: "backup"},
+		{name: "double dash", args: []string{"backup", "--"}, want: ""},
+	}
+
+	for _, tt := range tests {
+		tt := tt
+		t.Run(tt.name, func(t *testing.T) {
+			t.Parallel()
+
+			got := commandFromArgs(tt.args)
+			if got != tt.want {
+				t.Fatalf("commandFromArgs(%#v) = %q, want %q", tt.args, got, tt.want)
+			}
+		})
+	}
+}
+
 // indexOf returns the position of s in slice, or -1 if not found.
 func indexOf(slice []string, s string) int {
 	for i, v := range slice {

cmd/contract_test.go 🔗

@@ -0,0 +1,116 @@
+package cmd
+
+import (
+	"testing"
+
+	"git.secluded.site/keld/internal/restic"
+)
+
+func TestWrappedCommandsExist(t *testing.T) {
+	t.Parallel()
+
+	for _, wc := range wrappedCommands {
+		wc := wc
+		t.Run(wc.Name, func(t *testing.T) {
+			t.Parallel()
+
+			if _, ok := restic.Commands[wc.Name]; !ok {
+				t.Fatalf("wrapped command %q not found in restic.Commands", wc.Name)
+			}
+		})
+	}
+}
+
+func TestAllResticCommandsClassified(t *testing.T) {
+	t.Parallel()
+
+	wrapped := make(map[string]struct{}, len(wrappedCommands))
+	for _, wc := range wrappedCommands {
+		wrapped[wc.Name] = struct{}{}
+	}
+
+	for name := range restic.Commands {
+		name := name
+		if name == "global" {
+			continue
+		}
+
+		t.Run(name, func(t *testing.T) {
+			t.Parallel()
+
+			if _, ok := wrapped[name]; ok {
+				return
+			}
+			if _, ok := passthroughCommands[name]; ok {
+				return
+			}
+			t.Fatalf("restic command %q is neither wrapped nor listed in passthroughCommands", name)
+		})
+	}
+}
+
+func TestFlagContracts(t *testing.T) {
+	t.Parallel()
+
+	for _, fc := range flagContracts {
+		fc := fc
+		t.Run(fc.Command+"/"+fc.Name, func(t *testing.T) {
+			t.Parallel()
+
+			cmd, ok := restic.Commands[fc.Command]
+			if !ok {
+				t.Fatalf("command %q not found in restic.Commands", fc.Command)
+			}
+
+			var found bool
+			for _, opt := range cmd.Options {
+				if opt.Name == fc.Name {
+					found = true
+					if opt.IsBool != fc.IsBool {
+						t.Errorf("IsBool mismatch for %s/%s: got %v, want %v", fc.Command, fc.Name, opt.IsBool, fc.IsBool)
+					}
+					if opt.Repeatable != fc.Repeatable {
+						t.Errorf("Repeatable mismatch for %s/%s: got %v, want %v", fc.Command, fc.Name, opt.Repeatable, fc.Repeatable)
+					}
+					break
+				}
+			}
+			if !found {
+				t.Fatalf("flag %q not found in restic command %q", fc.Name, fc.Command)
+			}
+		})
+	}
+}
+
+// TestKeyAliasesTargetsExist verifies that the config key aliases (which are
+// unexported in the config package) target flags that actually exist in
+// restic's global options. The aliases are hardcoded here to mirror the
+// source of truth in internal/config/config.go.
+func TestKeyAliasesTargetsExist(t *testing.T) {
+	t.Parallel()
+
+	aliases := map[string]string{
+		"repository": "repo",
+	}
+
+	global, ok := restic.Commands["global"]
+	if !ok {
+		t.Fatal("restic.Commands missing 'global' entry")
+	}
+
+	globalFlags := make(map[string]struct{}, len(global.Options))
+	for _, opt := range global.Options {
+		globalFlags[opt.Name] = struct{}{}
+	}
+
+	for key, target := range aliases {
+		key, target := key, target
+		t.Run(key+"->"+target, func(t *testing.T) {
+			t.Parallel()
+
+			if _, ok := globalFlags[target]; !ok {
+				t.Fatalf("keyAlias %q -> %q: target flag %q not found in global options", key, target, target)
+			}
+		})
+	}
+}

cmd/gen-restic-cmds/main.go 🔗

@@ -30,6 +30,7 @@ type option struct {
 	Default     string
 	Description string
 	Repeatable  bool
+	IsBool      bool
 }
 
 // Regexes for the small subset of troff macros cobra emits.
@@ -195,6 +196,7 @@ func parseOptionLine(line string, opt **option, cmd *command) {
 			def := strings.Trim(parts[1], "[]\"")
 			(*opt).Default = def
 			(*opt).Repeatable = strings.Contains(parts[1], "[]") && def == ""
+			(*opt).IsBool = def == "false" || def == "true"
 		}
 
 		// Split the flag spec on "," to handle "-n, --dry-run" form.
@@ -252,7 +254,7 @@ var Commands = map[string]Command{
 		Description: {{quote .Description}},
 		Options: []Option{
 {{- range .Options}}
-			{Name: {{quote .Name}}, Alias: {{quote .Alias}}, Default: {{quote .Default}}, Description: {{quote .Description}}, Repeatable: {{printf "%v" .Repeatable}}},
+			{Name: {{quote .Name}}, Alias: {{quote .Alias}}, Default: {{quote .Default}}, Description: {{quote .Description}}, Repeatable: {{printf "%v" .Repeatable}}, IsBool: {{printf "%v" .IsBool}}},
 {{- end}}
 		},
 	},

cmd/root.go 🔗

@@ -23,18 +23,6 @@ var (
 
 const overrideArgumentsKey = "_arguments"
 
-// menuItems defines the interactive command picker shown when keld is invoked
-// with no arguments.
-var menuItems = []menu.Item{
-	{Label: "backup", Hotkey: 'b'},
-	{Label: "restore", Hotkey: 'r'},
-	{Label: "snapshots", Hotkey: 's'},
-	{Label: "forget", Hotkey: 'f'},
-	{Label: "check", Hotkey: 'c'},
-	{Label: "init", Hotkey: 'i'},
-	{Label: "quit", Hotkey: 'q'},
-}
-
 var rootCmd = &cobra.Command{
 	Use:   "keld [preset] <command> [restic flags...]",
 	Short: "A friendly wrapper around restic",

cmd/wrapspec.go 🔗

@@ -0,0 +1,92 @@
+package cmd
+
+import "git.secluded.site/keld/internal/menu"
+
+// wrappedCommand describes a restic command that keld exposes in its
+// interactive menu and shell completions.
+type wrappedCommand struct {
+	Name   string
+	Hotkey rune
+}
+
+// wrappedCommands lists the restic commands keld actively wraps. Adding
+// an entry here automatically surfaces it in the TUI menu and shell
+// completions. The order determines menu display order.
+var wrappedCommands = []wrappedCommand{
+	{Name: "backup", Hotkey: 'b'},
+	{Name: "restore", Hotkey: 'r'},
+	{Name: "snapshots", Hotkey: 's'},
+	{Name: "forget", Hotkey: 'f'},
+	{Name: "check", Hotkey: 'c'},
+	{Name: "init", Hotkey: 'i'},
+}
+
+// menuItems is derived from wrappedCommands with a trailing "quit" entry.
+var menuItems = func() []menu.Item {
+	items := make([]menu.Item, 0, len(wrappedCommands)+1)
+	for _, wc := range wrappedCommands {
+		items = append(items, menu.Item{Label: wc.Name, Hotkey: wc.Hotkey})
+	}
+	items = append(items, menu.Item{Label: "quit", Hotkey: 'q'})
+	return items
+}()
+
+// knownCommands is derived from wrappedCommands for shell completion.
+var knownCommands = func() []string {
+	names := make([]string, len(wrappedCommands))
+	for i, wc := range wrappedCommands {
+		names[i] = wc.Name
+	}
+	return names
+}()
+
+// flagContract describes a restic flag that keld semantically depends on
+// (e.g. used in HasFlag checks or keyAliases).
+type flagContract struct {
+	Command    string
+	Name       string
+	IsBool     bool
+	Repeatable bool
+}
+
+// flagContracts lists flags keld's wrapper logic hardcodes assumptions
+// about. Contract tests verify these still match the generated data.
+var flagContracts = []flagContract{
+	{Command: "restore", Name: "target", IsBool: false, Repeatable: false},
+	{Command: "global", Name: "repo", IsBool: false, Repeatable: false},
+}
+
+// passthroughCommands lists restic commands that keld does not actively wrap
+// but should still allow through without error. Contract tests verify that
+// every command in restic.Commands is either wrapped or listed here.
+var passthroughCommands = map[string]struct{}{
+	"cache":            {},
+	"cat":              {},
+	"copy":             {},
+	"diff":             {},
+	"dump":             {},
+	"features":         {},
+	"find":             {},
+	"generate":         {},
+	"key":              {},
+	"key-add":          {},
+	"key-list":         {},
+	"key-passwd":       {},
+	"key-remove":       {},
+	"list":             {},
+	"ls":               {},
+	"migrate":          {},
+	"mount":            {},
+	"options":          {},
+	"prune":            {},
+	"recover":          {},
+	"repair":           {},
+	"repair-index":     {},
+	"repair-packs":     {},
+	"repair-snapshots": {},
+	"rewrite":          {},
+	"stats":            {},
+	"tag":              {},
+	"unlock":           {},
+	"version":          {},
+}

internal/restic/command.go 🔗

@@ -17,4 +17,5 @@ type Option struct {
 	Default     string // default value as printed in the man page
 	Description string
 	Repeatable  bool // true when the flag accepts multiple values (default "[]")
+	IsBool      bool // true for boolean flags (default "false" or "true")
 }

internal/restic/commands_gen.go 🔗

@@ -10,467 +10,467 @@ var Commands = map[string]Command{
 		Name:        "backup",
 		Description: "Create a new backup of files and/or directories",
 		Options: []Option{
-			{Name: "dry-run", Alias: "n", Default: "false", Description: "do not upload or write any data, just show what would be done", Repeatable: false},
-			{Name: "exclude", Alias: "e", Default: "", Description: "exclude a pattern (can be specified multiple times)", Repeatable: true},
-			{Name: "exclude-caches", Alias: "", Default: "false", Description: "excludes cache directories that are marked with a CACHEDIR.TAG file. See https://bford.info/cachedir/ for the Cache Directory Tagging Standard", Repeatable: false},
-			{Name: "exclude-file", Alias: "", Default: "", Description: "read exclude patterns from a file (can be specified multiple times)", Repeatable: true},
-			{Name: "exclude-if-present", Alias: "", Default: "", Description: "takes filename[:header], exclude contents of directories containing filename (except filename itself) if header of that file is as provided (can be specified multiple times)", Repeatable: true},
-			{Name: "exclude-larger-than", Alias: "", Default: "", Description: "max size of the files to be backed up (allowed suffixes: k/K, m/M, g/G, t/T)", Repeatable: false},
-			{Name: "files-from", Alias: "", Default: "", Description: "read the files to backup from file (can be combined with file args; can be specified multiple times)", Repeatable: true},
-			{Name: "files-from-raw", Alias: "", Default: "", Description: "read the files to backup from file (can be combined with file args; can be specified multiple times)", Repeatable: true},
-			{Name: "files-from-verbatim", Alias: "", Default: "", Description: "read the files to backup from file (can be combined with file args; can be specified multiple times)", Repeatable: true},
-			{Name: "force", Alias: "f", Default: "false", Description: "force re-reading the source files/directories (overrides the \"parent\" flag)", Repeatable: false},
-			{Name: "group-by", Alias: "g", Default: "host,paths", Description: "group snapshots by host, paths and/or tags, separated by comma (disable grouping with '')", Repeatable: false},
-			{Name: "help", Alias: "h", Default: "false", Description: "help for backup", Repeatable: false},
-			{Name: "host", Alias: "H", Default: "", Description: "set the hostname for the snapshot manually (default: $RESTIC_HOST). To prevent an expensive rescan use the \"parent\" flag", Repeatable: false},
-			{Name: "iexclude", Alias: "", Default: "", Description: "same as --exclude pattern but ignores the casing of filenames", Repeatable: true},
-			{Name: "iexclude-file", Alias: "", Default: "", Description: "same as --exclude-file but ignores casing of filenames in patterns", Repeatable: true},
-			{Name: "ignore-ctime", Alias: "", Default: "false", Description: "ignore ctime changes when checking for modified files", Repeatable: false},
-			{Name: "ignore-inode", Alias: "", Default: "false", Description: "ignore inode number and ctime changes when checking for modified files", Repeatable: false},
-			{Name: "no-scan", Alias: "", Default: "false", Description: "do not run scanner to estimate size of backup", Repeatable: false},
-			{Name: "one-file-system", Alias: "x", Default: "false", Description: "exclude other file systems, don't cross filesystem boundaries and subvolumes", Repeatable: false},
-			{Name: "parent", Alias: "", Default: "", Description: "use this parent snapshot (default: latest snapshot in the group determined by --group-by and not newer than the timestamp determined by --time)", Repeatable: false},
-			{Name: "read-concurrency", Alias: "", Default: "0", Description: "read n files concurrently (default: $RESTIC_READ_CONCURRENCY or 2)", Repeatable: false},
-			{Name: "skip-if-unchanged", Alias: "", Default: "false", Description: "skip snapshot creation if identical to parent snapshot", Repeatable: false},
-			{Name: "stdin", Alias: "", Default: "false", Description: "read backup from stdin", Repeatable: false},
-			{Name: "stdin-filename", Alias: "", Default: "stdin", Description: "filename to use when reading from stdin", Repeatable: false},
-			{Name: "stdin-from-command", Alias: "", Default: "false", Description: "interpret arguments as command to execute and store its stdout", Repeatable: false},
-			{Name: "tag", Alias: "", Default: "", Description: "add tags for the new snapshot in the format tag[,tag,...] (can be specified multiple times)", Repeatable: true},
-			{Name: "time", Alias: "", Default: "", Description: "time of the backup (ex. '2012-11-01 22:08:41') (default: now)", Repeatable: false},
-			{Name: "with-atime", Alias: "", Default: "false", Description: "store the atime for all files and directories", Repeatable: false},
+			{Name: "dry-run", Alias: "n", Default: "false", Description: "do not upload or write any data, just show what would be done", Repeatable: false, IsBool: true},
+			{Name: "exclude", Alias: "e", Default: "", Description: "exclude a pattern (can be specified multiple times)", Repeatable: true, IsBool: false},
+			{Name: "exclude-caches", Alias: "", Default: "false", Description: "excludes cache directories that are marked with a CACHEDIR.TAG file. See https://bford.info/cachedir/ for the Cache Directory Tagging Standard", Repeatable: false, IsBool: true},
+			{Name: "exclude-file", Alias: "", Default: "", Description: "read exclude patterns from a file (can be specified multiple times)", Repeatable: true, IsBool: false},
+			{Name: "exclude-if-present", Alias: "", Default: "", Description: "takes filename[:header], exclude contents of directories containing filename (except filename itself) if header of that file is as provided (can be specified multiple times)", Repeatable: true, IsBool: false},
+			{Name: "exclude-larger-than", Alias: "", Default: "", Description: "max size of the files to be backed up (allowed suffixes: k/K, m/M, g/G, t/T)", Repeatable: false, IsBool: false},
+			{Name: "files-from", Alias: "", Default: "", Description: "read the files to backup from file (can be combined with file args; can be specified multiple times)", Repeatable: true, IsBool: false},
+			{Name: "files-from-raw", Alias: "", Default: "", Description: "read the files to backup from file (can be combined with file args; can be specified multiple times)", Repeatable: true, IsBool: false},
+			{Name: "files-from-verbatim", Alias: "", Default: "", Description: "read the files to backup from file (can be combined with file args; can be specified multiple times)", Repeatable: true, IsBool: false},
+			{Name: "force", Alias: "f", Default: "false", Description: "force re-reading the source files/directories (overrides the \"parent\" flag)", Repeatable: false, IsBool: true},
+			{Name: "group-by", Alias: "g", Default: "host,paths", Description: "group snapshots by host, paths and/or tags, separated by comma (disable grouping with '')", Repeatable: false, IsBool: false},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for backup", Repeatable: false, IsBool: true},
+			{Name: "host", Alias: "H", Default: "", Description: "set the hostname for the snapshot manually (default: $RESTIC_HOST). To prevent an expensive rescan use the \"parent\" flag", Repeatable: false, IsBool: false},
+			{Name: "iexclude", Alias: "", Default: "", Description: "same as --exclude pattern but ignores the casing of filenames", Repeatable: true, IsBool: false},
+			{Name: "iexclude-file", Alias: "", Default: "", Description: "same as --exclude-file but ignores casing of filenames in patterns", Repeatable: true, IsBool: false},
+			{Name: "ignore-ctime", Alias: "", Default: "false", Description: "ignore ctime changes when checking for modified files", Repeatable: false, IsBool: true},
+			{Name: "ignore-inode", Alias: "", Default: "false", Description: "ignore inode number and ctime changes when checking for modified files", Repeatable: false, IsBool: true},
+			{Name: "no-scan", Alias: "", Default: "false", Description: "do not run scanner to estimate size of backup", Repeatable: false, IsBool: true},
+			{Name: "one-file-system", Alias: "x", Default: "false", Description: "exclude other file systems, don't cross filesystem boundaries and subvolumes", Repeatable: false, IsBool: true},
+			{Name: "parent", Alias: "", Default: "", Description: "use this parent snapshot (default: latest snapshot in the group determined by --group-by and not newer than the timestamp determined by --time)", Repeatable: false, IsBool: false},
+			{Name: "read-concurrency", Alias: "", Default: "0", Description: "read n files concurrently (default: $RESTIC_READ_CONCURRENCY or 2)", Repeatable: false, IsBool: false},
+			{Name: "skip-if-unchanged", Alias: "", Default: "false", Description: "skip snapshot creation if identical to parent snapshot", Repeatable: false, IsBool: true},
+			{Name: "stdin", Alias: "", Default: "false", Description: "read backup from stdin", Repeatable: false, IsBool: true},
+			{Name: "stdin-filename", Alias: "", Default: "stdin", Description: "filename to use when reading from stdin", Repeatable: false, IsBool: false},
+			{Name: "stdin-from-command", Alias: "", Default: "false", Description: "interpret arguments as command to execute and store its stdout", Repeatable: false, IsBool: true},
+			{Name: "tag", Alias: "", Default: "", Description: "add tags for the new snapshot in the format tag[,tag,...] (can be specified multiple times)", Repeatable: true, IsBool: false},
+			{Name: "time", Alias: "", Default: "", Description: "time of the backup (ex. '2012-11-01 22:08:41') (default: now)", Repeatable: false, IsBool: false},
+			{Name: "with-atime", Alias: "", Default: "false", Description: "store the atime for all files and directories", Repeatable: false, IsBool: true},
 		},
 	},
 	"cache": {
 		Name:        "cache",
 		Description: "Operate on local cache directories",
 		Options: []Option{
-			{Name: "cleanup", Alias: "", Default: "false", Description: "remove old cache directories", Repeatable: false},
-			{Name: "help", Alias: "h", Default: "false", Description: "help for cache", Repeatable: false},
-			{Name: "max-age", Alias: "", Default: "30", Description: "max age in days for cache directories to be considered old", Repeatable: false},
-			{Name: "no-size", Alias: "", Default: "false", Description: "do not output the size of the cache directories", Repeatable: false},
+			{Name: "cleanup", Alias: "", Default: "false", Description: "remove old cache directories", Repeatable: false, IsBool: true},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for cache", Repeatable: false, IsBool: true},
+			{Name: "max-age", Alias: "", Default: "30", Description: "max age in days for cache directories to be considered old", Repeatable: false, IsBool: false},
+			{Name: "no-size", Alias: "", Default: "false", Description: "do not output the size of the cache directories", Repeatable: false, IsBool: true},
 		},
 	},
 	"cat": {
 		Name:        "cat",
 		Description: "Print internal objects to stdout",
 		Options: []Option{
-			{Name: "help", Alias: "h", Default: "false", Description: "help for cat", Repeatable: false},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for cat", Repeatable: false, IsBool: true},
 		},
 	},
 	"check": {
 		Name:        "check",
 		Description: "Check the repository for errors",
 		Options: []Option{
-			{Name: "help", Alias: "h", Default: "false", Description: "help for check", Repeatable: false},
-			{Name: "read-data", Alias: "", Default: "false", Description: "read all data blobs", Repeatable: false},
-			{Name: "read-data-subset", Alias: "", Default: "", Description: "read a subset of data packs, specified as 'n/t' for specific part, or either 'x%' or 'x.y%' or a size in bytes with suffixes k/K, m/M, g/G, t/T for a random subset", Repeatable: false},
-			{Name: "with-cache", Alias: "", Default: "false", Description: "use existing cache, only read uncached data from repository", Repeatable: false},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for check", Repeatable: false, IsBool: true},
+			{Name: "read-data", Alias: "", Default: "false", Description: "read all data blobs", Repeatable: false, IsBool: true},
+			{Name: "read-data-subset", Alias: "", Default: "", Description: "read a subset of data packs, specified as 'n/t' for specific part, or either 'x%' or 'x.y%' or a size in bytes with suffixes k/K, m/M, g/G, t/T for a random subset", Repeatable: false, IsBool: false},
+			{Name: "with-cache", Alias: "", Default: "false", Description: "use existing cache, only read uncached data from repository", Repeatable: false, IsBool: true},
 		},
 	},
 	"copy": {
 		Name:        "copy",
 		Description: "Copy snapshots from one repository to another",
 		Options: []Option{
-			{Name: "from-insecure-no-password", Alias: "", Default: "false", Description: "use an empty password for the source repository (insecure)", Repeatable: false},
-			{Name: "from-key-hint", Alias: "", Default: "", Description: "key ID of key to try decrypting the source repository first (default: $RESTIC_FROM_KEY_HINT)", Repeatable: false},
-			{Name: "from-password-command", Alias: "", Default: "", Description: "shell command to obtain the source repository password from (default: $RESTIC_FROM_PASSWORD_COMMAND)", Repeatable: false},
-			{Name: "from-password-file", Alias: "", Default: "", Description: "file to read the source repository password from (default: $RESTIC_FROM_PASSWORD_FILE)", Repeatable: false},
-			{Name: "from-repo", Alias: "", Default: "", Description: "source repository to copy snapshots from (default: $RESTIC_FROM_REPOSITORY)", Repeatable: false},
-			{Name: "from-repository-file", Alias: "", Default: "", Description: "file from which to read the source repository location to copy snapshots from (default: $RESTIC_FROM_REPOSITORY_FILE)", Repeatable: false},
-			{Name: "help", Alias: "h", Default: "false", Description: "help for copy", Repeatable: false},
-			{Name: "host", Alias: "H", Default: "", Description: "only consider snapshots for this host (can be specified multiple times) (default: $RESTIC_HOST)", Repeatable: true},
-			{Name: "path", Alias: "", Default: "", Description: "only consider snapshots including this (absolute) path (can be specified multiple times, snapshots must include all specified paths)", Repeatable: true},
-			{Name: "tag", Alias: "", Default: "", Description: "only consider snapshots including tag[,tag,...] (can be specified multiple times)", Repeatable: true},
+			{Name: "from-insecure-no-password", Alias: "", Default: "false", Description: "use an empty password for the source repository (insecure)", Repeatable: false, IsBool: true},
+			{Name: "from-key-hint", Alias: "", Default: "", Description: "key ID of key to try decrypting the source repository first (default: $RESTIC_FROM_KEY_HINT)", Repeatable: false, IsBool: false},
+			{Name: "from-password-command", Alias: "", Default: "", Description: "shell command to obtain the source repository password from (default: $RESTIC_FROM_PASSWORD_COMMAND)", Repeatable: false, IsBool: false},
+			{Name: "from-password-file", Alias: "", Default: "", Description: "file to read the source repository password from (default: $RESTIC_FROM_PASSWORD_FILE)", Repeatable: false, IsBool: false},
+			{Name: "from-repo", Alias: "", Default: "", Description: "source repository to copy snapshots from (default: $RESTIC_FROM_REPOSITORY)", Repeatable: false, IsBool: false},
+			{Name: "from-repository-file", Alias: "", Default: "", Description: "file from which to read the source repository location to copy snapshots from (default: $RESTIC_FROM_REPOSITORY_FILE)", Repeatable: false, IsBool: false},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for copy", Repeatable: false, IsBool: true},
+			{Name: "host", Alias: "H", Default: "", Description: "only consider snapshots for this host (can be specified multiple times) (default: $RESTIC_HOST)", Repeatable: true, IsBool: false},
+			{Name: "path", Alias: "", Default: "", Description: "only consider snapshots including this (absolute) path (can be specified multiple times, snapshots must include all specified paths)", Repeatable: true, IsBool: false},
+			{Name: "tag", Alias: "", Default: "", Description: "only consider snapshots including tag[,tag,...] (can be specified multiple times)", Repeatable: true, IsBool: false},
 		},
 	},
 	"diff": {
 		Name:        "diff",
 		Description: "Show differences between two snapshots",
 		Options: []Option{
-			{Name: "help", Alias: "h", Default: "false", Description: "help for diff", Repeatable: false},
-			{Name: "metadata", Alias: "", Default: "false", Description: "print changes in metadata", Repeatable: false},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for diff", Repeatable: false, IsBool: true},
+			{Name: "metadata", Alias: "", Default: "false", Description: "print changes in metadata", Repeatable: false, IsBool: true},
 		},
 	},
 	"dump": {
 		Name:        "dump",
 		Description: "Print a backed-up file to stdout",
 		Options: []Option{
-			{Name: "archive", Alias: "a", Default: "tar", Description: "set archive format as \"tar\" or \"zip\"", Repeatable: false},
-			{Name: "help", Alias: "h", Default: "false", Description: "help for dump", Repeatable: false},
-			{Name: "host", Alias: "H", Default: "", Description: "only consider snapshots for this host, when snapshot ID \"latest\" is given (can be specified multiple times) (default: $RESTIC_HOST)", Repeatable: true},
-			{Name: "path", Alias: "", Default: "", Description: "only consider snapshots including this (absolute) path, when snapshot ID \"latest\" is given (can be specified multiple times, snapshots must include all specified paths)", Repeatable: true},
-			{Name: "tag", Alias: "", Default: "", Description: "only consider snapshots including tag[,tag,...], when snapshot ID \"latest\" is given (can be specified multiple times)", Repeatable: true},
-			{Name: "target", Alias: "t", Default: "", Description: "write the output to target path", Repeatable: false},
+			{Name: "archive", Alias: "a", Default: "tar", Description: "set archive format as \"tar\" or \"zip\"", Repeatable: false, IsBool: false},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for dump", Repeatable: false, IsBool: true},
+			{Name: "host", Alias: "H", Default: "", Description: "only consider snapshots for this host, when snapshot ID \"latest\" is given (can be specified multiple times) (default: $RESTIC_HOST)", Repeatable: true, IsBool: false},
+			{Name: "path", Alias: "", Default: "", Description: "only consider snapshots including this (absolute) path, when snapshot ID \"latest\" is given (can be specified multiple times, snapshots must include all specified paths)", Repeatable: true, IsBool: false},
+			{Name: "tag", Alias: "", Default: "", Description: "only consider snapshots including tag[,tag,...], when snapshot ID \"latest\" is given (can be specified multiple times)", Repeatable: true, IsBool: false},
+			{Name: "target", Alias: "t", Default: "", Description: "write the output to target path", Repeatable: false, IsBool: false},
 		},
 	},
 	"features": {
 		Name:        "features",
 		Description: "Print list of feature flags",
 		Options: []Option{
-			{Name: "help", Alias: "h", Default: "false", Description: "help for features", Repeatable: false},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for features", Repeatable: false, IsBool: true},
 		},
 	},
 	"find": {
 		Name:        "find",
 		Description: "Find a file, a directory or restic IDs",
 		Options: []Option{
-			{Name: "blob", Alias: "", Default: "false", Description: "pattern is a blob-ID", Repeatable: false},
-			{Name: "help", Alias: "h", Default: "false", Description: "help for find", Repeatable: false},
-			{Name: "host", Alias: "H", Default: "", Description: "only consider snapshots for this host (can be specified multiple times) (default: $RESTIC_HOST)", Repeatable: true},
-			{Name: "human-readable", Alias: "", Default: "false", Description: "print sizes in human readable format", Repeatable: false},
-			{Name: "ignore-case", Alias: "i", Default: "false", Description: "ignore case for pattern", Repeatable: false},
-			{Name: "long", Alias: "l", Default: "false", Description: "use a long listing format showing size and mode", Repeatable: false},
-			{Name: "newest", Alias: "N", Default: "", Description: "newest modification date/time", Repeatable: false},
-			{Name: "oldest", Alias: "O", Default: "", Description: "oldest modification date/time", Repeatable: false},
-			{Name: "pack", Alias: "", Default: "false", Description: "pattern is a pack-ID", Repeatable: false},
-			{Name: "path", Alias: "", Default: "", Description: "only consider snapshots including this (absolute) path (can be specified multiple times, snapshots must include all specified paths)", Repeatable: true},
-			{Name: "reverse", Alias: "R", Default: "false", Description: "reverse sort order oldest to newest", Repeatable: false},
-			{Name: "show-pack-id", Alias: "", Default: "false", Description: "display the pack-ID the blobs belong to (with --blob or --tree)", Repeatable: false},
-			{Name: "snapshot", Alias: "s", Default: "", Description: "snapshot id to search in (can be given multiple times)", Repeatable: true},
-			{Name: "tag", Alias: "", Default: "", Description: "only consider snapshots including tag[,tag,...] (can be specified multiple times)", Repeatable: true},
-			{Name: "tree", Alias: "", Default: "false", Description: "pattern is a tree-ID", Repeatable: false},
+			{Name: "blob", Alias: "", Default: "false", Description: "pattern is a blob-ID", Repeatable: false, IsBool: true},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for find", Repeatable: false, IsBool: true},
+			{Name: "host", Alias: "H", Default: "", Description: "only consider snapshots for this host (can be specified multiple times) (default: $RESTIC_HOST)", Repeatable: true, IsBool: false},
+			{Name: "human-readable", Alias: "", Default: "false", Description: "print sizes in human readable format", Repeatable: false, IsBool: true},
+			{Name: "ignore-case", Alias: "i", Default: "false", Description: "ignore case for pattern", Repeatable: false, IsBool: true},
+			{Name: "long", Alias: "l", Default: "false", Description: "use a long listing format showing size and mode", Repeatable: false, IsBool: true},
+			{Name: "newest", Alias: "N", Default: "", Description: "newest modification date/time", Repeatable: false, IsBool: false},
+			{Name: "oldest", Alias: "O", Default: "", Description: "oldest modification date/time", Repeatable: false, IsBool: false},
+			{Name: "pack", Alias: "", Default: "false", Description: "pattern is a pack-ID", Repeatable: false, IsBool: true},
+			{Name: "path", Alias: "", Default: "", Description: "only consider snapshots including this (absolute) path (can be specified multiple times, snapshots must include all specified paths)", Repeatable: true, IsBool: false},
+			{Name: "reverse", Alias: "R", Default: "false", Description: "reverse sort order oldest to newest", Repeatable: false, IsBool: true},
+			{Name: "show-pack-id", Alias: "", Default: "false", Description: "display the pack-ID the blobs belong to (with --blob or --tree)", Repeatable: false, IsBool: true},
+			{Name: "snapshot", Alias: "s", Default: "", Description: "snapshot id to search in (can be given multiple times)", Repeatable: true, IsBool: false},
+			{Name: "tag", Alias: "", Default: "", Description: "only consider snapshots including tag[,tag,...] (can be specified multiple times)", Repeatable: true, IsBool: false},
+			{Name: "tree", Alias: "", Default: "false", Description: "pattern is a tree-ID", Repeatable: false, IsBool: true},
 		},
 	},
 	"forget": {
 		Name:        "forget",
 		Description: "Remove snapshots from the repository",
 		Options: []Option{
-			{Name: "keep-last", Alias: "l", Default: "0", Description: "keep the last n snapshots (use 'unlimited' to keep all snapshots)", Repeatable: false},
-			{Name: "keep-hourly", Alias: "H", Default: "0", Description: "keep the last n hourly snapshots (use 'unlimited' to keep all hourly snapshots)", Repeatable: false},
-			{Name: "keep-daily", Alias: "d", Default: "0", Description: "keep the last n daily snapshots (use 'unlimited' to keep all daily snapshots)", Repeatable: false},
-			{Name: "keep-weekly", Alias: "w", Default: "0", Description: "keep the last n weekly snapshots (use 'unlimited' to keep all weekly snapshots)", Repeatable: false},
-			{Name: "keep-monthly", Alias: "m", Default: "0", Description: "keep the last n monthly snapshots (use 'unlimited' to keep all monthly snapshots)", Repeatable: false},
-			{Name: "keep-yearly", Alias: "y", Default: "0", Description: "keep the last n yearly snapshots (use 'unlimited' to keep all yearly snapshots)", Repeatable: false},
-			{Name: "keep-within", Alias: "", Default: "", Description: "keep snapshots that are newer than duration (eg. 1y5m7d2h) relative to the latest snapshot", Repeatable: false},
-			{Name: "keep-within-hourly", Alias: "", Default: "", Description: "keep hourly snapshots that are newer than duration (eg. 1y5m7d2h) relative to the latest snapshot", Repeatable: false},
-			{Name: "keep-within-daily", Alias: "", Default: "", Description: "keep daily snapshots that are newer than duration (eg. 1y5m7d2h) relative to the latest snapshot", Repeatable: false},
-			{Name: "keep-within-weekly", Alias: "", Default: "", Description: "keep weekly snapshots that are newer than duration (eg. 1y5m7d2h) relative to the latest snapshot", Repeatable: false},
-			{Name: "keep-within-monthly", Alias: "", Default: "", Description: "keep monthly snapshots that are newer than duration (eg. 1y5m7d2h) relative to the latest snapshot", Repeatable: false},
-			{Name: "keep-within-yearly", Alias: "", Default: "", Description: "keep yearly snapshots that are newer than duration (eg. 1y5m7d2h) relative to the latest snapshot", Repeatable: false},
-			{Name: "keep-tag", Alias: "", Default: "", Description: "keep snapshots with this taglist (can be specified multiple times)", Repeatable: true},
-			{Name: "unsafe-allow-remove-all", Alias: "", Default: "false", Description: "allow deleting all snapshots of a snapshot group", Repeatable: false},
-			{Name: "host", Alias: "", Default: "", Description: "only consider snapshots for this host (can be specified multiple times) (default: $RESTIC_HOST)", Repeatable: true},
-			{Name: "tag", Alias: "", Default: "", Description: "only consider snapshots including tag[,tag,...] (can be specified multiple times)", Repeatable: true},
-			{Name: "path", Alias: "", Default: "", Description: "only consider snapshots including this (absolute) path (can be specified multiple times, snapshots must include all specified paths)", Repeatable: true},
-			{Name: "compact", Alias: "c", Default: "false", Description: "use compact output format", Repeatable: false},
-			{Name: "group-by", Alias: "g", Default: "host,paths", Description: "group snapshots by host, paths and/or tags, separated by comma (disable grouping with '')", Repeatable: false},
-			{Name: "dry-run", Alias: "n", Default: "false", Description: "do not delete anything, just print what would be done", Repeatable: false},
-			{Name: "prune", Alias: "", Default: "false", Description: "automatically run the 'prune' command if snapshots have been removed", Repeatable: false},
-			{Name: "max-unused", Alias: "", Default: "5%", Description: "tolerate given limit of unused data (absolute value in bytes with suffixes k/K, m/M, g/G, t/T, a value in % or the word 'unlimited')", Repeatable: false},
-			{Name: "max-repack-size", Alias: "", Default: "", Description: "stop after repacking this much data in total (allowed suffixes for size: k/K, m/M, g/G, t/T)", Repeatable: false},
-			{Name: "repack-cacheable-only", Alias: "", Default: "false", Description: "only repack packs which are cacheable", Repeatable: false},
-			{Name: "repack-small", Alias: "", Default: "false", Description: "repack pack files below 80% of target pack size", Repeatable: false},
-			{Name: "repack-uncompressed", Alias: "", Default: "false", Description: "repack all uncompressed data", Repeatable: false},
-			{Name: "repack-smaller-than", Alias: "", Default: "", Description: "pack below-limit packfiles (allowed suffixes: k/K, m/M)", Repeatable: false},
-			{Name: "help", Alias: "h", Default: "false", Description: "help for forget", Repeatable: false},
+			{Name: "keep-last", Alias: "l", Default: "0", Description: "keep the last n snapshots (use 'unlimited' to keep all snapshots)", Repeatable: false, IsBool: false},
+			{Name: "keep-hourly", Alias: "H", Default: "0", Description: "keep the last n hourly snapshots (use 'unlimited' to keep all hourly snapshots)", Repeatable: false, IsBool: false},
+			{Name: "keep-daily", Alias: "d", Default: "0", Description: "keep the last n daily snapshots (use 'unlimited' to keep all daily snapshots)", Repeatable: false, IsBool: false},
+			{Name: "keep-weekly", Alias: "w", Default: "0", Description: "keep the last n weekly snapshots (use 'unlimited' to keep all weekly snapshots)", Repeatable: false, IsBool: false},
+			{Name: "keep-monthly", Alias: "m", Default: "0", Description: "keep the last n monthly snapshots (use 'unlimited' to keep all monthly snapshots)", Repeatable: false, IsBool: false},
+			{Name: "keep-yearly", Alias: "y", Default: "0", Description: "keep the last n yearly snapshots (use 'unlimited' to keep all yearly snapshots)", Repeatable: false, IsBool: false},
+			{Name: "keep-within", Alias: "", Default: "", Description: "keep snapshots that are newer than duration (eg. 1y5m7d2h) relative to the latest snapshot", Repeatable: false, IsBool: false},
+			{Name: "keep-within-hourly", Alias: "", Default: "", Description: "keep hourly snapshots that are newer than duration (eg. 1y5m7d2h) relative to the latest snapshot", Repeatable: false, IsBool: false},
+			{Name: "keep-within-daily", Alias: "", Default: "", Description: "keep daily snapshots that are newer than duration (eg. 1y5m7d2h) relative to the latest snapshot", Repeatable: false, IsBool: false},
+			{Name: "keep-within-weekly", Alias: "", Default: "", Description: "keep weekly snapshots that are newer than duration (eg. 1y5m7d2h) relative to the latest snapshot", Repeatable: false, IsBool: false},
+			{Name: "keep-within-monthly", Alias: "", Default: "", Description: "keep monthly snapshots that are newer than duration (eg. 1y5m7d2h) relative to the latest snapshot", Repeatable: false, IsBool: false},
+			{Name: "keep-within-yearly", Alias: "", Default: "", Description: "keep yearly snapshots that are newer than duration (eg. 1y5m7d2h) relative to the latest snapshot", Repeatable: false, IsBool: false},
+			{Name: "keep-tag", Alias: "", Default: "", Description: "keep snapshots with this taglist (can be specified multiple times)", Repeatable: true, IsBool: false},
+			{Name: "unsafe-allow-remove-all", Alias: "", Default: "false", Description: "allow deleting all snapshots of a snapshot group", Repeatable: false, IsBool: true},
+			{Name: "host", Alias: "", Default: "", Description: "only consider snapshots for this host (can be specified multiple times) (default: $RESTIC_HOST)", Repeatable: true, IsBool: false},
+			{Name: "tag", Alias: "", Default: "", Description: "only consider snapshots including tag[,tag,...] (can be specified multiple times)", Repeatable: true, IsBool: false},
+			{Name: "path", Alias: "", Default: "", Description: "only consider snapshots including this (absolute) path (can be specified multiple times, snapshots must include all specified paths)", Repeatable: true, IsBool: false},
+			{Name: "compact", Alias: "c", Default: "false", Description: "use compact output format", Repeatable: false, IsBool: true},
+			{Name: "group-by", Alias: "g", Default: "host,paths", Description: "group snapshots by host, paths and/or tags, separated by comma (disable grouping with '')", Repeatable: false, IsBool: false},
+			{Name: "dry-run", Alias: "n", Default: "false", Description: "do not delete anything, just print what would be done", Repeatable: false, IsBool: true},
+			{Name: "prune", Alias: "", Default: "false", Description: "automatically run the 'prune' command if snapshots have been removed", Repeatable: false, IsBool: true},
+			{Name: "max-unused", Alias: "", Default: "5%", Description: "tolerate given limit of unused data (absolute value in bytes with suffixes k/K, m/M, g/G, t/T, a value in % or the word 'unlimited')", Repeatable: false, IsBool: false},
+			{Name: "max-repack-size", Alias: "", Default: "", Description: "stop after repacking this much data in total (allowed suffixes for size: k/K, m/M, g/G, t/T)", Repeatable: false, IsBool: false},
+			{Name: "repack-cacheable-only", Alias: "", Default: "false", Description: "only repack packs which are cacheable", Repeatable: false, IsBool: true},
+			{Name: "repack-small", Alias: "", Default: "false", Description: "repack pack files below 80% of target pack size", Repeatable: false, IsBool: true},
+			{Name: "repack-uncompressed", Alias: "", Default: "false", Description: "repack all uncompressed data", Repeatable: false, IsBool: true},
+			{Name: "repack-smaller-than", Alias: "", Default: "", Description: "pack below-limit packfiles (allowed suffixes: k/K, m/M)", Repeatable: false, IsBool: false},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for forget", Repeatable: false, IsBool: true},
 		},
 	},
 	"generate": {
 		Name:        "generate",
 		Description: "Generate manual pages and auto-completion files (bash, fish, zsh, powershell)",
 		Options: []Option{
-			{Name: "bash-completion", Alias: "", Default: "", Description: "write bash completion file (- for stdout)", Repeatable: false},
-			{Name: "fish-completion", Alias: "", Default: "", Description: "write fish completion file (- for stdout)", Repeatable: false},
-			{Name: "help", Alias: "h", Default: "false", Description: "help for generate", Repeatable: false},
-			{Name: "man", Alias: "", Default: "", Description: "write man pages to directory", Repeatable: false},
-			{Name: "powershell-completion", Alias: "", Default: "", Description: "write powershell completion file (- for stdout)", Repeatable: false},
-			{Name: "zsh-completion", Alias: "", Default: "", Description: "write zsh completion file (- for stdout)", Repeatable: false},
+			{Name: "bash-completion", Alias: "", Default: "", Description: "write bash completion file (- for stdout)", Repeatable: false, IsBool: false},
+			{Name: "fish-completion", Alias: "", Default: "", Description: "write fish completion file (- for stdout)", Repeatable: false, IsBool: false},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for generate", Repeatable: false, IsBool: true},
+			{Name: "man", Alias: "", Default: "", Description: "write man pages to directory", Repeatable: false, IsBool: false},
+			{Name: "powershell-completion", Alias: "", Default: "", Description: "write powershell completion file (- for stdout)", Repeatable: false, IsBool: false},
+			{Name: "zsh-completion", Alias: "", Default: "", Description: "write zsh completion file (- for stdout)", Repeatable: false, IsBool: false},
 		},
 	},
 	"global": {
 		Name:        "global",
 		Description: "Backup and restore files",
 		Options: []Option{
-			{Name: "cacert", Alias: "", Default: "", Description: "file to load root certificates from (default: use system certificates or $RESTIC_CACERT)", Repeatable: true},
-			{Name: "cache-dir", Alias: "", Default: "", Description: "set the cache directory. (default: use system default cache directory)", Repeatable: false},
-			{Name: "cleanup-cache", Alias: "", Default: "false", Description: "auto remove old cache directories", Repeatable: false},
-			{Name: "compression", Alias: "", Default: "auto", Description: "compression mode (only available for repository format version 2), one of (auto|off|max) (default: $RESTIC_COMPRESSION)", Repeatable: false},
-			{Name: "help", Alias: "h", Default: "false", Description: "help for restic", Repeatable: false},
-			{Name: "http-user-agent", Alias: "", Default: "", Description: "set a http user agent for outgoing http requests", Repeatable: false},
-			{Name: "insecure-no-password", Alias: "", Default: "false", Description: "use an empty password for the repository, must be passed to every restic command (insecure)", Repeatable: false},
-			{Name: "insecure-tls", Alias: "", Default: "false", Description: "skip TLS certificate verification when connecting to the repository (insecure)", Repeatable: false},
-			{Name: "json", Alias: "", Default: "false", Description: "set output mode to JSON for commands that support it", Repeatable: false},
-			{Name: "key-hint", Alias: "", Default: "", Description: "key ID of key to try decrypting first (default: $RESTIC_KEY_HINT)", Repeatable: false},
-			{Name: "limit-download", Alias: "", Default: "0", Description: "limits downloads to a maximum rate in KiB/s. (default: unlimited)", Repeatable: false},
-			{Name: "limit-upload", Alias: "", Default: "0", Description: "limits uploads to a maximum rate in KiB/s. (default: unlimited)", Repeatable: false},
-			{Name: "no-cache", Alias: "", Default: "false", Description: "do not use a local cache", Repeatable: false},
-			{Name: "no-extra-verify", Alias: "", Default: "false", Description: "skip additional verification of data before upload (see documentation)", Repeatable: false},
-			{Name: "no-lock", Alias: "", Default: "false", Description: "do not lock the repository, this allows some operations on read-only repositories", Repeatable: false},
-			{Name: "option", Alias: "o", Default: "", Description: "set extended option (key=value, can be specified multiple times)", Repeatable: true},
-			{Name: "pack-size", Alias: "", Default: "0", Description: "set target pack size in MiB, created pack files may be larger (default: $RESTIC_PACK_SIZE)", Repeatable: false},
-			{Name: "password-command", Alias: "", Default: "", Description: "shell command to obtain the repository password from (default: $RESTIC_PASSWORD_COMMAND)", Repeatable: false},
-			{Name: "password-file", Alias: "p", Default: "", Description: "file to read the repository password from (default: $RESTIC_PASSWORD_FILE)", Repeatable: false},
-			{Name: "quiet", Alias: "q", Default: "false", Description: "do not output comprehensive progress report", Repeatable: false},
-			{Name: "repo", Alias: "r", Default: "", Description: "repository to backup to or restore from (default: $RESTIC_REPOSITORY)", Repeatable: false},
-			{Name: "repository-file", Alias: "", Default: "", Description: "file to read the repository location from (default: $RESTIC_REPOSITORY_FILE)", Repeatable: false},
-			{Name: "retry-lock", Alias: "", Default: "0s", Description: "retry to lock the repository if it is already locked, takes a value like 5m or 2h (default: no retries)", Repeatable: false},
-			{Name: "stuck-request-timeout", Alias: "", Default: "5m0s", Description: "duration after which to retry stuck requests", Repeatable: false},
-			{Name: "tls-client-cert", Alias: "", Default: "", Description: "path to a file containing PEM encoded TLS client certificate and private key (default: $RESTIC_TLS_CLIENT_CERT)", Repeatable: false},
-			{Name: "verbose", Alias: "v", Default: "0", Description: "be verbose (specify multiple times or a level using --verbose=n, max level/times is 2)", Repeatable: false},
+			{Name: "cacert", Alias: "", Default: "", Description: "file to load root certificates from (default: use system certificates or $RESTIC_CACERT)", Repeatable: true, IsBool: false},
+			{Name: "cache-dir", Alias: "", Default: "", Description: "set the cache directory. (default: use system default cache directory)", Repeatable: false, IsBool: false},
+			{Name: "cleanup-cache", Alias: "", Default: "false", Description: "auto remove old cache directories", Repeatable: false, IsBool: true},
+			{Name: "compression", Alias: "", Default: "auto", Description: "compression mode (only available for repository format version 2), one of (auto|off|max) (default: $RESTIC_COMPRESSION)", Repeatable: false, IsBool: false},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for restic", Repeatable: false, IsBool: true},
+			{Name: "http-user-agent", Alias: "", Default: "", Description: "set a http user agent for outgoing http requests", Repeatable: false, IsBool: false},
+			{Name: "insecure-no-password", Alias: "", Default: "false", Description: "use an empty password for the repository, must be passed to every restic command (insecure)", Repeatable: false, IsBool: true},
+			{Name: "insecure-tls", Alias: "", Default: "false", Description: "skip TLS certificate verification when connecting to the repository (insecure)", Repeatable: false, IsBool: true},
+			{Name: "json", Alias: "", Default: "false", Description: "set output mode to JSON for commands that support it", Repeatable: false, IsBool: true},
+			{Name: "key-hint", Alias: "", Default: "", Description: "key ID of key to try decrypting first (default: $RESTIC_KEY_HINT)", Repeatable: false, IsBool: false},
+			{Name: "limit-download", Alias: "", Default: "0", Description: "limits downloads to a maximum rate in KiB/s. (default: unlimited)", Repeatable: false, IsBool: false},
+			{Name: "limit-upload", Alias: "", Default: "0", Description: "limits uploads to a maximum rate in KiB/s. (default: unlimited)", Repeatable: false, IsBool: false},
+			{Name: "no-cache", Alias: "", Default: "false", Description: "do not use a local cache", Repeatable: false, IsBool: true},
+			{Name: "no-extra-verify", Alias: "", Default: "false", Description: "skip additional verification of data before upload (see documentation)", Repeatable: false, IsBool: true},
+			{Name: "no-lock", Alias: "", Default: "false", Description: "do not lock the repository, this allows some operations on read-only repositories", Repeatable: false, IsBool: true},
+			{Name: "option", Alias: "o", Default: "", Description: "set extended option (key=value, can be specified multiple times)", Repeatable: true, IsBool: false},
+			{Name: "pack-size", Alias: "", Default: "0", Description: "set target pack size in MiB, created pack files may be larger (default: $RESTIC_PACK_SIZE)", Repeatable: false, IsBool: false},
+			{Name: "password-command", Alias: "", Default: "", Description: "shell command to obtain the repository password from (default: $RESTIC_PASSWORD_COMMAND)", Repeatable: false, IsBool: false},
+			{Name: "password-file", Alias: "p", Default: "", Description: "file to read the repository password from (default: $RESTIC_PASSWORD_FILE)", Repeatable: false, IsBool: false},
+			{Name: "quiet", Alias: "q", Default: "false", Description: "do not output comprehensive progress report", Repeatable: false, IsBool: true},
+			{Name: "repo", Alias: "r", Default: "", Description: "repository to backup to or restore from (default: $RESTIC_REPOSITORY)", Repeatable: false, IsBool: false},
+			{Name: "repository-file", Alias: "", Default: "", Description: "file to read the repository location from (default: $RESTIC_REPOSITORY_FILE)", Repeatable: false, IsBool: false},
+			{Name: "retry-lock", Alias: "", Default: "0s", Description: "retry to lock the repository if it is already locked, takes a value like 5m or 2h (default: no retries)", Repeatable: false, IsBool: false},
+			{Name: "stuck-request-timeout", Alias: "", Default: "5m0s", Description: "duration after which to retry stuck requests", Repeatable: false, IsBool: false},
+			{Name: "tls-client-cert", Alias: "", Default: "", Description: "path to a file containing PEM encoded TLS client certificate and private key (default: $RESTIC_TLS_CLIENT_CERT)", Repeatable: false, IsBool: false},
+			{Name: "verbose", Alias: "v", Default: "0", Description: "be verbose (specify multiple times or a level using --verbose=n, max level/times is 2)", Repeatable: false, IsBool: false},
 		},
 	},
 	"init": {
 		Name:        "init",
 		Description: "Initialize a new repository",
 		Options: []Option{
-			{Name: "copy-chunker-params", Alias: "", Default: "false", Description: "copy chunker parameters from the secondary repository (useful with the copy command)", Repeatable: false},
-			{Name: "from-insecure-no-password", Alias: "", Default: "false", Description: "use an empty password for the source repository (insecure)", Repeatable: false},
-			{Name: "from-key-hint", Alias: "", Default: "", Description: "key ID of key to try decrypting the source repository first (default: $RESTIC_FROM_KEY_HINT)", Repeatable: false},
-			{Name: "from-password-command", Alias: "", Default: "", Description: "shell command to obtain the source repository password from (default: $RESTIC_FROM_PASSWORD_COMMAND)", Repeatable: false},
-			{Name: "from-password-file", Alias: "", Default: "", Description: "file to read the source repository password from (default: $RESTIC_FROM_PASSWORD_FILE)", Repeatable: false},
-			{Name: "from-repo", Alias: "", Default: "", Description: "source repository to copy chunker parameters from (default: $RESTIC_FROM_REPOSITORY)", Repeatable: false},
-			{Name: "from-repository-file", Alias: "", Default: "", Description: "file from which to read the source repository location to copy chunker parameters from (default: $RESTIC_FROM_REPOSITORY_FILE)", Repeatable: false},
-			{Name: "help", Alias: "h", Default: "false", Description: "help for init", Repeatable: false},
-			{Name: "repository-version", Alias: "", Default: "stable", Description: "repository format version to use, allowed values are a format version, 'latest' and 'stable'", Repeatable: false},
+			{Name: "copy-chunker-params", Alias: "", Default: "false", Description: "copy chunker parameters from the secondary repository (useful with the copy command)", Repeatable: false, IsBool: true},
+			{Name: "from-insecure-no-password", Alias: "", Default: "false", Description: "use an empty password for the source repository (insecure)", Repeatable: false, IsBool: true},
+			{Name: "from-key-hint", Alias: "", Default: "", Description: "key ID of key to try decrypting the source repository first (default: $RESTIC_FROM_KEY_HINT)", Repeatable: false, IsBool: false},
+			{Name: "from-password-command", Alias: "", Default: "", Description: "shell command to obtain the source repository password from (default: $RESTIC_FROM_PASSWORD_COMMAND)", Repeatable: false, IsBool: false},
+			{Name: "from-password-file", Alias: "", Default: "", Description: "file to read the source repository password from (default: $RESTIC_FROM_PASSWORD_FILE)", Repeatable: false, IsBool: false},
+			{Name: "from-repo", Alias: "", Default: "", Description: "source repository to copy chunker parameters from (default: $RESTIC_FROM_REPOSITORY)", Repeatable: false, IsBool: false},
+			{Name: "from-repository-file", Alias: "", Default: "", Description: "file from which to read the source repository location to copy chunker parameters from (default: $RESTIC_FROM_REPOSITORY_FILE)", Repeatable: false, IsBool: false},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for init", Repeatable: false, IsBool: true},
+			{Name: "repository-version", Alias: "", Default: "stable", Description: "repository format version to use, allowed values are a format version, 'latest' and 'stable'", Repeatable: false, IsBool: false},
 		},
 	},
 	"key": {
 		Name:        "key",
 		Description: "Manage keys (passwords)",
 		Options: []Option{
-			{Name: "help", Alias: "h", Default: "false", Description: "help for key", Repeatable: false},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for key", Repeatable: false, IsBool: true},
 		},
 	},
 	"key-add": {
 		Name:        "key-add",
 		Description: "Add a new key (password) to the repository; returns the new key ID",
 		Options: []Option{
-			{Name: "help", Alias: "h", Default: "false", Description: "help for add", Repeatable: false},
-			{Name: "host", Alias: "", Default: "", Description: "the hostname for new key", Repeatable: false},
-			{Name: "new-insecure-no-password", Alias: "", Default: "false", Description: "add an empty password for the repository (insecure)", Repeatable: false},
-			{Name: "new-password-file", Alias: "", Default: "", Description: "file from which to read the new password", Repeatable: false},
-			{Name: "user", Alias: "", Default: "", Description: "the username for new key", Repeatable: false},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for add", Repeatable: false, IsBool: true},
+			{Name: "host", Alias: "", Default: "", Description: "the hostname for new key", Repeatable: false, IsBool: false},
+			{Name: "new-insecure-no-password", Alias: "", Default: "false", Description: "add an empty password for the repository (insecure)", Repeatable: false, IsBool: true},
+			{Name: "new-password-file", Alias: "", Default: "", Description: "file from which to read the new password", Repeatable: false, IsBool: false},
+			{Name: "user", Alias: "", Default: "", Description: "the username for new key", Repeatable: false, IsBool: false},
 		},
 	},
 	"key-list": {
 		Name:        "key-list",
 		Description: "List keys (passwords)",
 		Options: []Option{
-			{Name: "help", Alias: "h", Default: "false", Description: "help for list", Repeatable: false},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for list", Repeatable: false, IsBool: true},
 		},
 	},
 	"key-passwd": {
 		Name:        "key-passwd",
 		Description: "Change key (password); creates a new key ID and removes the old key ID, returns new key ID",
 		Options: []Option{
-			{Name: "help", Alias: "h", Default: "false", Description: "help for passwd", Repeatable: false},
-			{Name: "host", Alias: "", Default: "", Description: "the hostname for new key", Repeatable: false},
-			{Name: "new-insecure-no-password", Alias: "", Default: "false", Description: "add an empty password for the repository (insecure)", Repeatable: false},
-			{Name: "new-password-file", Alias: "", Default: "", Description: "file from which to read the new password", Repeatable: false},
-			{Name: "user", Alias: "", Default: "", Description: "the username for new key", Repeatable: false},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for passwd", Repeatable: false, IsBool: true},
+			{Name: "host", Alias: "", Default: "", Description: "the hostname for new key", Repeatable: false, IsBool: false},
+			{Name: "new-insecure-no-password", Alias: "", Default: "false", Description: "add an empty password for the repository (insecure)", Repeatable: false, IsBool: true},
+			{Name: "new-password-file", Alias: "", Default: "", Description: "file from which to read the new password", Repeatable: false, IsBool: false},
+			{Name: "user", Alias: "", Default: "", Description: "the username for new key", Repeatable: false, IsBool: false},
 		},
 	},
 	"key-remove": {
 		Name:        "key-remove",
 		Description: "Remove key ID (password) from the repository.",
 		Options: []Option{
-			{Name: "help", Alias: "h", Default: "false", Description: "help for remove", Repeatable: false},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for remove", Repeatable: false, IsBool: true},
 		},
 	},
 	"list": {
 		Name:        "list",
 		Description: "List objects in the repository",
 		Options: []Option{
-			{Name: "help", Alias: "h", Default: "false", Description: "help for list", Repeatable: false},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for list", Repeatable: false, IsBool: true},
 		},
 	},
 	"ls": {
 		Name:        "ls",
 		Description: "List files in a snapshot",
 		Options: []Option{
-			{Name: "help", Alias: "h", Default: "false", Description: "help for ls", Repeatable: false},
-			{Name: "host", Alias: "H", Default: "", Description: "only consider snapshots for this host, when snapshot ID \"latest\" is given (can be specified multiple times) (default: $RESTIC_HOST)", Repeatable: true},
-			{Name: "human-readable", Alias: "", Default: "false", Description: "print sizes in human readable format", Repeatable: false},
-			{Name: "long", Alias: "l", Default: "false", Description: "use a long listing format showing size and mode", Repeatable: false},
-			{Name: "ncdu", Alias: "", Default: "false", Description: "output NCDU export format (pipe into 'ncdu -f -')", Repeatable: false},
-			{Name: "path", Alias: "", Default: "", Description: "only consider snapshots including this (absolute) path, when snapshot ID \"latest\" is given (can be specified multiple times, snapshots must include all specified paths)", Repeatable: true},
-			{Name: "recursive", Alias: "", Default: "false", Description: "include files in subfolders of the listed directories", Repeatable: false},
-			{Name: "reverse", Alias: "", Default: "false", Description: "reverse sorted output", Repeatable: false},
-			{Name: "sort", Alias: "s", Default: "name", Description: "sort output by (name|size|time=mtime|atime|ctime|extension)", Repeatable: false},
-			{Name: "tag", Alias: "", Default: "", Description: "only consider snapshots including tag[,tag,...], when snapshot ID \"latest\" is given (can be specified multiple times)", Repeatable: true},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for ls", Repeatable: false, IsBool: true},
+			{Name: "host", Alias: "H", Default: "", Description: "only consider snapshots for this host, when snapshot ID \"latest\" is given (can be specified multiple times) (default: $RESTIC_HOST)", Repeatable: true, IsBool: false},
+			{Name: "human-readable", Alias: "", Default: "false", Description: "print sizes in human readable format", Repeatable: false, IsBool: true},
+			{Name: "long", Alias: "l", Default: "false", Description: "use a long listing format showing size and mode", Repeatable: false, IsBool: true},
+			{Name: "ncdu", Alias: "", Default: "false", Description: "output NCDU export format (pipe into 'ncdu -f -')", Repeatable: false, IsBool: true},
+			{Name: "path", Alias: "", Default: "", Description: "only consider snapshots including this (absolute) path, when snapshot ID \"latest\" is given (can be specified multiple times, snapshots must include all specified paths)", Repeatable: true, IsBool: false},
+			{Name: "recursive", Alias: "", Default: "false", Description: "include files in subfolders of the listed directories", Repeatable: false, IsBool: true},
+			{Name: "reverse", Alias: "", Default: "false", Description: "reverse sorted output", Repeatable: false, IsBool: true},
+			{Name: "sort", Alias: "s", Default: "name", Description: "sort output by (name|size|time=mtime|atime|ctime|extension)", Repeatable: false, IsBool: false},
+			{Name: "tag", Alias: "", Default: "", Description: "only consider snapshots including tag[,tag,...], when snapshot ID \"latest\" is given (can be specified multiple times)", Repeatable: true, IsBool: false},
 		},
 	},
 	"migrate": {
 		Name:        "migrate",
 		Description: "Apply migrations",
 		Options: []Option{
-			{Name: "force", Alias: "f", Default: "false", Description: "apply a migration a second time", Repeatable: false},
-			{Name: "help", Alias: "h", Default: "false", Description: "help for migrate", Repeatable: false},
+			{Name: "force", Alias: "f", Default: "false", Description: "apply a migration a second time", Repeatable: false, IsBool: true},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for migrate", Repeatable: false, IsBool: true},
 		},
 	},
 	"mount": {
 		Name:        "mount",
 		Description: "Mount the repository",
 		Options: []Option{
-			{Name: "allow-other", Alias: "", Default: "false", Description: "allow other users to access the data in the mounted directory", Repeatable: false},
-			{Name: "help", Alias: "h", Default: "false", Description: "help for mount", Repeatable: false},
-			{Name: "host", Alias: "H", Default: "", Description: "only consider snapshots for this host (can be specified multiple times) (default: $RESTIC_HOST)", Repeatable: true},
-			{Name: "no-default-permissions", Alias: "", Default: "false", Description: "for 'allow-other', ignore Unix permissions and allow users to read all snapshot files", Repeatable: false},
-			{Name: "owner-root", Alias: "", Default: "false", Description: "use 'root' as the owner of files and dirs", Repeatable: false},
-			{Name: "path", Alias: "", Default: "", Description: "only consider snapshots including this (absolute) path (can be specified multiple times, snapshots must include all specified paths)", Repeatable: true},
-			{Name: "path-template", Alias: "", Default: "", Description: "set template for path names (can be specified multiple times)", Repeatable: true},
-			{Name: "tag", Alias: "", Default: "", Description: "only consider snapshots including tag[,tag,...] (can be specified multiple times)", Repeatable: true},
-			{Name: "time-template", Alias: "", Default: "2006-01-02T15:04:05Z07:00", Description: "set template to use for times", Repeatable: false},
+			{Name: "allow-other", Alias: "", Default: "false", Description: "allow other users to access the data in the mounted directory", Repeatable: false, IsBool: true},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for mount", Repeatable: false, IsBool: true},
+			{Name: "host", Alias: "H", Default: "", Description: "only consider snapshots for this host (can be specified multiple times) (default: $RESTIC_HOST)", Repeatable: true, IsBool: false},
+			{Name: "no-default-permissions", Alias: "", Default: "false", Description: "for 'allow-other', ignore Unix permissions and allow users to read all snapshot files", Repeatable: false, IsBool: true},
+			{Name: "owner-root", Alias: "", Default: "false", Description: "use 'root' as the owner of files and dirs", Repeatable: false, IsBool: true},
+			{Name: "path", Alias: "", Default: "", Description: "only consider snapshots including this (absolute) path (can be specified multiple times, snapshots must include all specified paths)", Repeatable: true, IsBool: false},
+			{Name: "path-template", Alias: "", Default: "", Description: "set template for path names (can be specified multiple times)", Repeatable: true, IsBool: false},
+			{Name: "tag", Alias: "", Default: "", Description: "only consider snapshots including tag[,tag,...] (can be specified multiple times)", Repeatable: true, IsBool: false},
+			{Name: "time-template", Alias: "", Default: "2006-01-02T15:04:05Z07:00", Description: "set template to use for times", Repeatable: false, IsBool: false},
 		},
 	},
 	"options": {
 		Name:        "options",
 		Description: "Print list of extended options",
 		Options: []Option{
-			{Name: "help", Alias: "h", Default: "false", Description: "help for options", Repeatable: false},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for options", Repeatable: false, IsBool: true},
 		},
 	},
 	"prune": {
 		Name:        "prune",
 		Description: "Remove unneeded data from the repository",
 		Options: []Option{
-			{Name: "dry-run", Alias: "n", Default: "false", Description: "do not modify the repository, just print what would be done", Repeatable: false},
-			{Name: "help", Alias: "h", Default: "false", Description: "help for prune", Repeatable: false},
-			{Name: "max-repack-size", Alias: "", Default: "", Description: "stop after repacking this much data in total (allowed suffixes for size: k/K, m/M, g/G, t/T)", Repeatable: false},
-			{Name: "max-unused", Alias: "", Default: "5%", Description: "tolerate given limit of unused data (absolute value in bytes with suffixes k/K, m/M, g/G, t/T, a value in % or the word 'unlimited')", Repeatable: false},
-			{Name: "repack-cacheable-only", Alias: "", Default: "false", Description: "only repack packs which are cacheable", Repeatable: false},
-			{Name: "repack-small", Alias: "", Default: "false", Description: "repack pack files below 80% of target pack size", Repeatable: false},
-			{Name: "repack-smaller-than", Alias: "", Default: "", Description: "pack below-limit packfiles (allowed suffixes: k/K, m/M)", Repeatable: false},
-			{Name: "repack-uncompressed", Alias: "", Default: "false", Description: "repack all uncompressed data", Repeatable: false},
-			{Name: "unsafe-recover-no-free-space", Alias: "", Default: "", Description: "UNSAFE, READ THE DOCUMENTATION BEFORE USING! Try to recover a repository stuck with no free space. Do not use without trying out 'prune --max-repack-size 0' first.", Repeatable: false},
+			{Name: "dry-run", Alias: "n", Default: "false", Description: "do not modify the repository, just print what would be done", Repeatable: false, IsBool: true},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for prune", Repeatable: false, IsBool: true},
+			{Name: "max-repack-size", Alias: "", Default: "", Description: "stop after repacking this much data in total (allowed suffixes for size: k/K, m/M, g/G, t/T)", Repeatable: false, IsBool: false},
+			{Name: "max-unused", Alias: "", Default: "5%", Description: "tolerate given limit of unused data (absolute value in bytes with suffixes k/K, m/M, g/G, t/T, a value in % or the word 'unlimited')", Repeatable: false, IsBool: false},
+			{Name: "repack-cacheable-only", Alias: "", Default: "false", Description: "only repack packs which are cacheable", Repeatable: false, IsBool: true},
+			{Name: "repack-small", Alias: "", Default: "false", Description: "repack pack files below 80% of target pack size", Repeatable: false, IsBool: true},
+			{Name: "repack-smaller-than", Alias: "", Default: "", Description: "pack below-limit packfiles (allowed suffixes: k/K, m/M)", Repeatable: false, IsBool: false},
+			{Name: "repack-uncompressed", Alias: "", Default: "false", Description: "repack all uncompressed data", Repeatable: false, IsBool: true},
+			{Name: "unsafe-recover-no-free-space", Alias: "", Default: "", Description: "UNSAFE, READ THE DOCUMENTATION BEFORE USING! Try to recover a repository stuck with no free space. Do not use without trying out 'prune --max-repack-size 0' first.", Repeatable: false, IsBool: false},
 		},
 	},
 	"recover": {
 		Name:        "recover",
 		Description: "Recover data from the repository not referenced by snapshots",
 		Options: []Option{
-			{Name: "help", Alias: "h", Default: "false", Description: "help for recover", Repeatable: false},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for recover", Repeatable: false, IsBool: true},
 		},
 	},
 	"repair": {
 		Name:        "repair",
 		Description: "Repair the repository",
 		Options: []Option{
-			{Name: "help", Alias: "h", Default: "false", Description: "help for repair", Repeatable: false},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for repair", Repeatable: false, IsBool: true},
 		},
 	},
 	"repair-index": {
 		Name:        "repair-index",
 		Description: "Build a new index",
 		Options: []Option{
-			{Name: "help", Alias: "h", Default: "false", Description: "help for index", Repeatable: false},
-			{Name: "read-all-packs", Alias: "", Default: "false", Description: "read all pack files to generate new index from scratch", Repeatable: false},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for index", Repeatable: false, IsBool: true},
+			{Name: "read-all-packs", Alias: "", Default: "false", Description: "read all pack files to generate new index from scratch", Repeatable: false, IsBool: true},
 		},
 	},
 	"repair-packs": {
 		Name:        "repair-packs",
 		Description: "Salvage damaged pack files",
 		Options: []Option{
-			{Name: "help", Alias: "h", Default: "false", Description: "help for packs", Repeatable: false},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for packs", Repeatable: false, IsBool: true},
 		},
 	},
 	"repair-snapshots": {
 		Name:        "repair-snapshots",
 		Description: "Repair snapshots",
 		Options: []Option{
-			{Name: "dry-run", Alias: "n", Default: "false", Description: "do not do anything, just print what would be done", Repeatable: false},
-			{Name: "forget", Alias: "", Default: "false", Description: "remove original snapshots after creating new ones", Repeatable: false},
-			{Name: "help", Alias: "h", Default: "false", Description: "help for snapshots", Repeatable: false},
-			{Name: "host", Alias: "H", Default: "", Description: "only consider snapshots for this host (can be specified multiple times) (default: $RESTIC_HOST)", Repeatable: true},
-			{Name: "path", Alias: "", Default: "", Description: "only consider snapshots including this (absolute) path (can be specified multiple times, snapshots must include all specified paths)", Repeatable: true},
-			{Name: "tag", Alias: "", Default: "", Description: "only consider snapshots including tag[,tag,...] (can be specified multiple times)", Repeatable: true},
+			{Name: "dry-run", Alias: "n", Default: "false", Description: "do not do anything, just print what would be done", Repeatable: false, IsBool: true},
+			{Name: "forget", Alias: "", Default: "false", Description: "remove original snapshots after creating new ones", Repeatable: false, IsBool: true},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for snapshots", Repeatable: false, IsBool: true},
+			{Name: "host", Alias: "H", Default: "", Description: "only consider snapshots for this host (can be specified multiple times) (default: $RESTIC_HOST)", Repeatable: true, IsBool: false},
+			{Name: "path", Alias: "", Default: "", Description: "only consider snapshots including this (absolute) path (can be specified multiple times, snapshots must include all specified paths)", Repeatable: true, IsBool: false},
+			{Name: "tag", Alias: "", Default: "", Description: "only consider snapshots including tag[,tag,...] (can be specified multiple times)", Repeatable: true, IsBool: false},
 		},
 	},
 	"restore": {
 		Name:        "restore",
 		Description: "Extract the data from a snapshot",
 		Options: []Option{
-			{Name: "delete", Alias: "", Default: "false", Description: "delete files from target directory if they do not exist in snapshot. Use '--dry-run -vv' to check what would be deleted", Repeatable: false},
-			{Name: "dry-run", Alias: "", Default: "false", Description: "do not write any data, just show what would be done", Repeatable: false},
-			{Name: "exclude", Alias: "e", Default: "", Description: "exclude a pattern (can be specified multiple times)", Repeatable: true},
-			{Name: "exclude-file", Alias: "", Default: "", Description: "read exclude patterns from a file (can be specified multiple times)", Repeatable: true},
-			{Name: "exclude-xattr", Alias: "", Default: "", Description: "exclude xattr by pattern (can be specified multiple times)", Repeatable: true},
-			{Name: "help", Alias: "h", Default: "false", Description: "help for restore", Repeatable: false},
-			{Name: "host", Alias: "H", Default: "", Description: "only consider snapshots for this host, when snapshot ID \"latest\" is given (can be specified multiple times) (default: $RESTIC_HOST)", Repeatable: true},
-			{Name: "iexclude", Alias: "", Default: "", Description: "same as --exclude pattern but ignores the casing of filenames", Repeatable: true},
-			{Name: "iexclude-file", Alias: "", Default: "", Description: "same as --exclude-file but ignores casing of filenames in patterns", Repeatable: true},
-			{Name: "iinclude", Alias: "", Default: "", Description: "same as --include pattern but ignores the casing of filenames", Repeatable: true},
-			{Name: "iinclude-file", Alias: "", Default: "", Description: "same as --include-file but ignores casing of filenames in patterns", Repeatable: true},
-			{Name: "include", Alias: "i", Default: "", Description: "include a pattern (can be specified multiple times)", Repeatable: true},
-			{Name: "include-file", Alias: "", Default: "", Description: "read include patterns from a file (can be specified multiple times)", Repeatable: true},
-			{Name: "include-xattr", Alias: "", Default: "", Description: "include xattr by pattern (can be specified multiple times)", Repeatable: true},
-			{Name: "overwrite", Alias: "", Default: "always", Description: "overwrite behavior, one of (always|if-changed|if-newer|never)", Repeatable: false},
-			{Name: "path", Alias: "", Default: "", Description: "only consider snapshots including this (absolute) path, when snapshot ID \"latest\" is given (can be specified multiple times, snapshots must include all specified paths)", Repeatable: true},
-			{Name: "sparse", Alias: "", Default: "false", Description: "restore files as sparse", Repeatable: false},
-			{Name: "tag", Alias: "", Default: "", Description: "only consider snapshots including tag[,tag,...], when snapshot ID \"latest\" is given (can be specified multiple times)", Repeatable: true},
-			{Name: "target", Alias: "t", Default: "", Description: "directory to extract data to", Repeatable: false},
-			{Name: "verify", Alias: "", Default: "false", Description: "verify restored files content", Repeatable: false},
+			{Name: "delete", Alias: "", Default: "false", Description: "delete files from target directory if they do not exist in snapshot. Use '--dry-run -vv' to check what would be deleted", Repeatable: false, IsBool: true},
+			{Name: "dry-run", Alias: "", Default: "false", Description: "do not write any data, just show what would be done", Repeatable: false, IsBool: true},
+			{Name: "exclude", Alias: "e", Default: "", Description: "exclude a pattern (can be specified multiple times)", Repeatable: true, IsBool: false},
+			{Name: "exclude-file", Alias: "", Default: "", Description: "read exclude patterns from a file (can be specified multiple times)", Repeatable: true, IsBool: false},
+			{Name: "exclude-xattr", Alias: "", Default: "", Description: "exclude xattr by pattern (can be specified multiple times)", Repeatable: true, IsBool: false},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for restore", Repeatable: false, IsBool: true},
+			{Name: "host", Alias: "H", Default: "", Description: "only consider snapshots for this host, when snapshot ID \"latest\" is given (can be specified multiple times) (default: $RESTIC_HOST)", Repeatable: true, IsBool: false},
+			{Name: "iexclude", Alias: "", Default: "", Description: "same as --exclude pattern but ignores the casing of filenames", Repeatable: true, IsBool: false},
+			{Name: "iexclude-file", Alias: "", Default: "", Description: "same as --exclude-file but ignores casing of filenames in patterns", Repeatable: true, IsBool: false},
+			{Name: "iinclude", Alias: "", Default: "", Description: "same as --include pattern but ignores the casing of filenames", Repeatable: true, IsBool: false},
+			{Name: "iinclude-file", Alias: "", Default: "", Description: "same as --include-file but ignores casing of filenames in patterns", Repeatable: true, IsBool: false},
+			{Name: "include", Alias: "i", Default: "", Description: "include a pattern (can be specified multiple times)", Repeatable: true, IsBool: false},
+			{Name: "include-file", Alias: "", Default: "", Description: "read include patterns from a file (can be specified multiple times)", Repeatable: true, IsBool: false},
+			{Name: "include-xattr", Alias: "", Default: "", Description: "include xattr by pattern (can be specified multiple times)", Repeatable: true, IsBool: false},
+			{Name: "overwrite", Alias: "", Default: "always", Description: "overwrite behavior, one of (always|if-changed|if-newer|never)", Repeatable: false, IsBool: false},
+			{Name: "path", Alias: "", Default: "", Description: "only consider snapshots including this (absolute) path, when snapshot ID \"latest\" is given (can be specified multiple times, snapshots must include all specified paths)", Repeatable: true, IsBool: false},
+			{Name: "sparse", Alias: "", Default: "false", Description: "restore files as sparse", Repeatable: false, IsBool: true},
+			{Name: "tag", Alias: "", Default: "", Description: "only consider snapshots including tag[,tag,...], when snapshot ID \"latest\" is given (can be specified multiple times)", Repeatable: true, IsBool: false},
+			{Name: "target", Alias: "t", Default: "", Description: "directory to extract data to", Repeatable: false, IsBool: false},
+			{Name: "verify", Alias: "", Default: "false", Description: "verify restored files content", Repeatable: false, IsBool: true},
 		},
 	},
 	"rewrite": {
 		Name:        "rewrite",
 		Description: "Rewrite snapshots to exclude unwanted files",
 		Options: []Option{
-			{Name: "dry-run", Alias: "n", Default: "false", Description: "do not do anything, just print what would be done", Repeatable: false},
-			{Name: "exclude", Alias: "e", Default: "", Description: "exclude a pattern (can be specified multiple times)", Repeatable: true},
-			{Name: "exclude-file", Alias: "", Default: "", Description: "read exclude patterns from a file (can be specified multiple times)", Repeatable: true},
-			{Name: "forget", Alias: "", Default: "false", Description: "remove original snapshots after creating new ones", Repeatable: false},
-			{Name: "help", Alias: "h", Default: "false", Description: "help for rewrite", Repeatable: false},
-			{Name: "host", Alias: "H", Default: "", Description: "only consider snapshots for this host (can be specified multiple times) (default: $RESTIC_HOST)", Repeatable: true},
-			{Name: "iexclude", Alias: "", Default: "", Description: "same as --exclude pattern but ignores the casing of filenames", Repeatable: true},
-			{Name: "iexclude-file", Alias: "", Default: "", Description: "same as --exclude-file but ignores casing of filenames in patterns", Repeatable: true},
-			{Name: "new-host", Alias: "", Default: "", Description: "replace hostname", Repeatable: false},
-			{Name: "new-time", Alias: "", Default: "", Description: "replace time of the backup", Repeatable: false},
-			{Name: "path", Alias: "", Default: "", Description: "only consider snapshots including this (absolute) path (can be specified multiple times, snapshots must include all specified paths)", Repeatable: true},
-			{Name: "snapshot-summary", Alias: "s", Default: "false", Description: "create snapshot summary record if it does not exist", Repeatable: false},
-			{Name: "tag", Alias: "", Default: "", Description: "only consider snapshots including tag[,tag,...] (can be specified multiple times)", Repeatable: true},
+			{Name: "dry-run", Alias: "n", Default: "false", Description: "do not do anything, just print what would be done", Repeatable: false, IsBool: true},
+			{Name: "exclude", Alias: "e", Default: "", Description: "exclude a pattern (can be specified multiple times)", Repeatable: true, IsBool: false},
+			{Name: "exclude-file", Alias: "", Default: "", Description: "read exclude patterns from a file (can be specified multiple times)", Repeatable: true, IsBool: false},
+			{Name: "forget", Alias: "", Default: "false", Description: "remove original snapshots after creating new ones", Repeatable: false, IsBool: true},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for rewrite", Repeatable: false, IsBool: true},
+			{Name: "host", Alias: "H", Default: "", Description: "only consider snapshots for this host (can be specified multiple times) (default: $RESTIC_HOST)", Repeatable: true, IsBool: false},
+			{Name: "iexclude", Alias: "", Default: "", Description: "same as --exclude pattern but ignores the casing of filenames", Repeatable: true, IsBool: false},
+			{Name: "iexclude-file", Alias: "", Default: "", Description: "same as --exclude-file but ignores casing of filenames in patterns", Repeatable: true, IsBool: false},
+			{Name: "new-host", Alias: "", Default: "", Description: "replace hostname", Repeatable: false, IsBool: false},
+			{Name: "new-time", Alias: "", Default: "", Description: "replace time of the backup", Repeatable: false, IsBool: false},
+			{Name: "path", Alias: "", Default: "", Description: "only consider snapshots including this (absolute) path (can be specified multiple times, snapshots must include all specified paths)", Repeatable: true, IsBool: false},
+			{Name: "snapshot-summary", Alias: "s", Default: "false", Description: "create snapshot summary record if it does not exist", Repeatable: false, IsBool: true},
+			{Name: "tag", Alias: "", Default: "", Description: "only consider snapshots including tag[,tag,...] (can be specified multiple times)", Repeatable: true, IsBool: false},
 		},
 	},
 	"snapshots": {
 		Name:        "snapshots",
 		Description: "List all snapshots",
 		Options: []Option{
-			{Name: "compact", Alias: "c", Default: "false", Description: "use compact output format", Repeatable: false},
-			{Name: "group-by", Alias: "g", Default: "", Description: "group snapshots by host, paths and/or tags, separated by comma", Repeatable: false},
-			{Name: "help", Alias: "h", Default: "false", Description: "help for snapshots", Repeatable: false},
-			{Name: "host", Alias: "H", Default: "", Description: "only consider snapshots for this host (can be specified multiple times) (default: $RESTIC_HOST)", Repeatable: true},
-			{Name: "latest", Alias: "", Default: "0", Description: "only show the last n snapshots for each host and path", Repeatable: false},
-			{Name: "path", Alias: "", Default: "", Description: "only consider snapshots including this (absolute) path (can be specified multiple times, snapshots must include all specified paths)", Repeatable: true},
-			{Name: "tag", Alias: "", Default: "", Description: "only consider snapshots including tag[,tag,...] (can be specified multiple times)", Repeatable: true},
+			{Name: "compact", Alias: "c", Default: "false", Description: "use compact output format", Repeatable: false, IsBool: true},
+			{Name: "group-by", Alias: "g", Default: "", Description: "group snapshots by host, paths and/or tags, separated by comma", Repeatable: false, IsBool: false},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for snapshots", Repeatable: false, IsBool: true},
+			{Name: "host", Alias: "H", Default: "", Description: "only consider snapshots for this host (can be specified multiple times) (default: $RESTIC_HOST)", Repeatable: true, IsBool: false},
+			{Name: "latest", Alias: "", Default: "0", Description: "only show the last n snapshots for each host and path", Repeatable: false, IsBool: false},
+			{Name: "path", Alias: "", Default: "", Description: "only consider snapshots including this (absolute) path (can be specified multiple times, snapshots must include all specified paths)", Repeatable: true, IsBool: false},
+			{Name: "tag", Alias: "", Default: "", Description: "only consider snapshots including tag[,tag,...] (can be specified multiple times)", Repeatable: true, IsBool: false},
 		},
 	},
 	"stats": {
 		Name:        "stats",
 		Description: "Scan the repository and show basic statistics",
 		Options: []Option{
-			{Name: "help", Alias: "h", Default: "false", Description: "help for stats", Repeatable: false},
-			{Name: "host", Alias: "H", Default: "", Description: "only consider snapshots for this host (can be specified multiple times) (default: $RESTIC_HOST)", Repeatable: true},
-			{Name: "mode", Alias: "", Default: "restore-size", Description: "counting mode: restore-size (default), files-by-contents, blobs-per-file or raw-data", Repeatable: false},
-			{Name: "path", Alias: "", Default: "", Description: "only consider snapshots including this (absolute) path (can be specified multiple times, snapshots must include all specified paths)", Repeatable: true},
-			{Name: "tag", Alias: "", Default: "", Description: "only consider snapshots including tag[,tag,...] (can be specified multiple times)", Repeatable: true},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for stats", Repeatable: false, IsBool: true},
+			{Name: "host", Alias: "H", Default: "", Description: "only consider snapshots for this host (can be specified multiple times) (default: $RESTIC_HOST)", Repeatable: true, IsBool: false},
+			{Name: "mode", Alias: "", Default: "restore-size", Description: "counting mode: restore-size (default), files-by-contents, blobs-per-file or raw-data", Repeatable: false, IsBool: false},
+			{Name: "path", Alias: "", Default: "", Description: "only consider snapshots including this (absolute) path (can be specified multiple times, snapshots must include all specified paths)", Repeatable: true, IsBool: false},
+			{Name: "tag", Alias: "", Default: "", Description: "only consider snapshots including tag[,tag,...] (can be specified multiple times)", Repeatable: true, IsBool: false},
 		},
 	},
 	"tag": {
 		Name:        "tag",
 		Description: "Modify tags on snapshots",
 		Options: []Option{
-			{Name: "add", Alias: "", Default: "", Description: "tags which will be added to the existing tags in the format tag[,tag,...] (can be given multiple times)", Repeatable: true},
-			{Name: "help", Alias: "h", Default: "false", Description: "help for tag", Repeatable: false},
-			{Name: "host", Alias: "H", Default: "", Description: "only consider snapshots for this host (can be specified multiple times) (default: $RESTIC_HOST)", Repeatable: true},
-			{Name: "path", Alias: "", Default: "", Description: "only consider snapshots including this (absolute) path (can be specified multiple times, snapshots must include all specified paths)", Repeatable: true},
-			{Name: "remove", Alias: "", Default: "", Description: "tags which will be removed from the existing tags in the format tag[,tag,...] (can be given multiple times)", Repeatable: true},
-			{Name: "set", Alias: "", Default: "", Description: "tags which will replace the existing tags in the format tag[,tag,...] (can be given multiple times)", Repeatable: true},
-			{Name: "tag", Alias: "", Default: "", Description: "only consider snapshots including tag[,tag,...] (can be specified multiple times)", Repeatable: true},
+			{Name: "add", Alias: "", Default: "", Description: "tags which will be added to the existing tags in the format tag[,tag,...] (can be given multiple times)", Repeatable: true, IsBool: false},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for tag", Repeatable: false, IsBool: true},
+			{Name: "host", Alias: "H", Default: "", Description: "only consider snapshots for this host (can be specified multiple times) (default: $RESTIC_HOST)", Repeatable: true, IsBool: false},
+			{Name: "path", Alias: "", Default: "", Description: "only consider snapshots including this (absolute) path (can be specified multiple times, snapshots must include all specified paths)", Repeatable: true, IsBool: false},
+			{Name: "remove", Alias: "", Default: "", Description: "tags which will be removed from the existing tags in the format tag[,tag,...] (can be given multiple times)", Repeatable: true, IsBool: false},
+			{Name: "set", Alias: "", Default: "", Description: "tags which will replace the existing tags in the format tag[,tag,...] (can be given multiple times)", Repeatable: true, IsBool: false},
+			{Name: "tag", Alias: "", Default: "", Description: "only consider snapshots including tag[,tag,...] (can be specified multiple times)", Repeatable: true, IsBool: false},
 		},
 	},
 	"unlock": {
 		Name:        "unlock",
 		Description: "Remove locks other processes created",
 		Options: []Option{
-			{Name: "help", Alias: "h", Default: "false", Description: "help for unlock", Repeatable: false},
-			{Name: "remove-all", Alias: "", Default: "false", Description: "remove all locks, even non-stale ones", Repeatable: false},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for unlock", Repeatable: false, IsBool: true},
+			{Name: "remove-all", Alias: "", Default: "false", Description: "remove all locks, even non-stale ones", Repeatable: false, IsBool: true},
 		},
 	},
 	"version": {
 		Name:        "version",
 		Description: "Print version information",
 		Options: []Option{
-			{Name: "help", Alias: "h", Default: "false", Description: "help for version", Repeatable: false},
+			{Name: "help", Alias: "h", Default: "false", Description: "help for version", Repeatable: false, IsBool: true},
 		},
 	},
 }