Home

tmpl @main - refs - log -
-
https://git.jolheiser.com/tmpl.git
Template automation
tree log patch
Major changes (#22) 1. Switch from TOML to YAML (oof) 2. Add a JSON schema for templates (anti-oof) 3. Add functionality for prompt defaults to be based on earlier prompt answers 4. Add env commands and setting/loading for tmpl-specific env 5. Use a more helpful testing library Co-authored-by: jolheiser <john.olheiser@gmail.com> Reviewed-on: https://git.jojodev.com/jolheiser/tmpl/pulls/22
jolheiser <john+jojodev@jolheiser.com>
2 years ago
34 changed files, 874 additions(+), 485 deletions(-)
M CLI.md -> CLI.md
diff --git a/CLI.md b/CLI.md
index c7c5a8dff80a2b743a70af340c1f4d5c8ca91d30..b9d864705f6b7164e117a588cee922922ca3fc17 100644
--- a/CLI.md
+++ b/CLI.md
@@ -11,6 +11,10 @@ [--registry|-r]=[value]
 [--source|-s]=[value]
 ```
 
+# DESCRIPTION
+
+Template automation
+
 **Usage**:
 
 ```
@@ -35,6 +39,14 @@
 ## env
 
 Show tmpl environment variables
+
+### set, add
+
+Set a tmpl environment variable (stored plaintext)
+
+### unset, delete
+
+Unsets a tmpl environment variable
 
 ## init
 
D DOCS.md
diff --git a/DOCS.md b/DOCS.md
deleted file mode 100644
index 005df0b563724c88cc5d4cdd6157eeef2b059cf6..0000000000000000000000000000000000000000
--- a/DOCS.md
+++ /dev/null
@@ -1,131 +0,0 @@
-# tmpl templates
-
-This documentation aims to cover FAQs and setup.
-
-## Setting up a template
-
-A "valid" tmpl template only requires two things
-
-1. A `template.toml` file in the root directory.
-2. A `template` directory that serves as the "root" of the template.
-
-## template.toml
-
-**NOTE:** The template.toml file will be expanded, though not with the full power of the template itself.  
-The template.toml file will only expand environment variables with syntax `$USER` or `${USER}`.  
-For full documentation on the syntax, see [os.ExpandEnv](https://golang.org/pkg/os/#ExpandEnv).
-
-When using the `--defaults` flag, no prompts will be shown and only default values will be used.  
-As another alternative, any environment variable that matches a key will bypass the prompt.  
-For example, `author` would have the corresponding environment variable `TMPL_VAR_AUTHOR`.
-
-```toml
-# Key-value pairs can be simple
-# The user will receive a basic prompt asking them to fill out the variable
-project = "my-project"
-
-# Extended properties MUST be added after any simple key-value pairs (due to how TOML works)
-
-# The "key" is enclosed in braces
-[author]
-# prompt is what will be shown to prompt the user
-prompt = "The name of the author of this project"
-# help would be extra information (generally seen by giving '?' to a prompt)
-help = "Who will be primarily writing this project"
-# default is the "value" part of the simple pair. This could be a suggested value
-default = "$USER"
-```
-
-## template directory
-
-This directory contains any and all files that are part of the template.
-
-Everything in this directory (including paths and file names!) will be executed as a [Go template](https://golang.org/pkg/text/template/).
-
-See the [documentation](https://golang.org/pkg/text/template/) for every available possibility, but some basic examples are...
-
-* A variable defined in template.toml (tmpl allows for keys to be called as a func or variable, whichever you prefer!)
-   * `{{project}}` or `{{.project}}`
-   * `{{author}}` or `{{.author}}`
-* Conditionally including something
-   * `{{if eq project ""}} something... {{end}}`
-
-### template helpers
-
-For a full list, see [helper.go](registry/helper.go)
-
-|Helper|Example|Output|
-|-----|-----|-----|
-|upper|`{{upper project}}`|`MY-PROJECT`|
-|lower|`{{lower project}}`|`my-project`|
-|title|`{{title project}}`|`My-Project`|
-|snake|`{{snake project}}`|`my_project`|
-|kebab|`{{kebab project}}`|`my-project`|
-|pascal|`{{pascal project}}`|`MyProject`|
-|camel|`{{camel project}}`|`myProject`|
-|env|`{{env "USER"}}`|The current user|
-|sep|`{{sep}}`|Filepath separator for current OS|
-|time}|`{{time "01/02/2006"}}`|`11/21/2020` - The time according to the given [format](https://flaviocopes.com/go-date-time-format/)|
-
-## Sources
-
-tmpl was designed to work with any local or git-based template. Unfortunately, in contrast to boilr, this means 
-it cannot be used with `user/repo` notation out of the box. 
-
-However, you _can_ set up a source (and subsequent env variable) to make it easier to use your preferred source while
-still allowing for others.
-
-### Setting up a source
-
-Let's set up a source for [Gitea](https://gitea.com)
-
-```
-tmpl source add https://gitea.com gitea
-```
-
-To use it, either pass it in with the `--source` flag
-
-```
-tmpl --source gitea download jolheiser/tmpls tmpls
-```
-
-Or set it as the env variable `TMPL_SOURCE`
-
-## Using a different branch
-
-By default, tmpl will want to use a branch called `main` in your repository.
-
-If you are using another branch as your default, you can set it as the env variable `TMPL_BRANCH`
-
-Alternatively, you can specify on the command-line with the `--branch` flag of the `download` command
-
-```
-tmpl --source gitea download --branch license jolheiser/tmpls license
-```
-The above command would download the [license](https://git.jojodev.com/jolheiser/tmpls/src/branch/license) template from `jolheiser/tmpls`
-
-## Putting it all together
-
-I realize that many users will be using GitHub, and most will likely still be using the `master` branch.
-
-1. Set up a source for GitHub
-   1. `tmpl source add https://github.com github`
-   2. Set the env variable `TMPL_SOURCE` to `github`
-2. Set the env variable `TMPL_BRANCH` to `master`
-3. Happy templating! `tmpl download user/repo repo`
-
-## Backup and Restore
-
-1. The simplest solution is to make a copy of your `registry.toml` (default: `~/.tmpl/registry.toml`).
-   * Once in the new location, you will need to use `tmpl restore`.
-   
-2. Alternatively, you can copy/paste the entire registry (default: `~/.tmpl`) and skip the restore step.
-
-## `.tmplkeep`
-
-Perhaps you are familiar with `.gitkeep` and its unofficial status in git. Git does not like empty directories, so usually
-a `.gitkeep` (or just `.keep`) file is added to retain the directory while keeping it effectively empty.
-
-tmpl instead uses `.tmplkeep` files for this purpose. The difference is, tmpl will **not** create the `.tmplkeep` file
-when the template is executed. This allows you to set up directory structures (for staging, examples, etc.) that
-will *actually* be empty after execution.
\ No newline at end of file
I FAQ.md
diff --git a/FAQ.md b/FAQ.md
new file mode 100644
index 0000000000000000000000000000000000000000..b6d813afde7e837d7febfbc90438b0470a39282b
--- /dev/null
+++ b/FAQ.md
@@ -0,0 +1,127 @@
+# tmpl templates
+
+This documentation aims to cover FAQs and setup.
+
+## Setting up a template
+
+A "valid" tmpl template only requires two things
+
+1. A `tmpl.yaml` file in the root directory.
+2. A `template` directory that serves as the "root" of the template.
+
+## tmpl.yaml
+
+**NOTE:** The tmpl.yaml file will be expanded, though not with the full power of the template itself.  
+The tmpl.yaml file will only expand environment variables with syntax `$USER` or `${USER}`.  
+For full documentation on the syntax, see [os.ExpandEnv](https://golang.org/pkg/os/#ExpandEnv).
+
+When using the `--defaults` flag, no prompts will be shown and only default values will be used.  
+As another alternative, any environment variable that matches a key will bypass the prompt.  
+For example, `author` would have the corresponding environment variable `TMPL_VAR_AUTHOR`.
+
+```yaml
+# tmpl.yaml
+# Write any template args here to prompt the user for, giving any defaults/options as applicable
+
+prompts:
+  - id: project                           # The unique ID for the prompt
+    label: Project Name                   # The prompt message/label
+    help: The name to use in the project  # Optional help message for the prompt
+    default: tmpl                         # Prompt default
+```
+
+## template directory
+
+This directory contains any and all files that are part of the template.
+
+Everything in this directory (including paths and file names!) will be executed as a [Go template](https://golang.org/pkg/text/template/).
+
+See the [documentation](https://golang.org/pkg/text/template/) for every available possibility, but some basic examples are...
+
+* An id defined in `tmpl.yaml` (tmpl allows for keys to be called as a func or variable, whichever you prefer!)
+   * `{{project}}` or `{{.project}}`
+   * `{{author}}` or `{{.author}}`
+* Conditionally including something
+   * `{{if eq project ""}} something... {{end}}`
+
+### template helpers
+
+For a full list, see [helper.go](registry/helper.go)
+
+| Helper      | Example                          | Output                                                                                                |
+|-------------|----------------------------------|-------------------------------------------------------------------------------------------------------|
+| upper       | `{{upper project}}`              | `MY-PROJECT`                                                                                          |
+| lower       | `{{lower project}}`              | `my-project`                                                                                          |
+| title       | `{{title project}}`              | `My-Project`                                                                                          |
+| snake       | `{{snake project}}`              | `my_project`                                                                                          |
+| kebab       | `{{kebab project}}`              | `my-project`                                                                                          |
+| pascal      | `{{pascal project}}`             | `MyProject`                                                                                           |
+| camel       | `{{camel project}}`              | `myProject`                                                                                           |
+| env         | `{{env "USER"}}`                 | The current user                                                                                      |
+| sep         | `{{sep}}`                        | Filepath separator for current OS                                                                     |
+| time        | `{{time "01/02/2006"}}`          | `11/21/2020` - The time according to the given [format](https://flaviocopes.com/go-date-time-format/) |
+| trim_prefix | `{{trim_prefix "foobar" "foo"}}` | `bar`                                                                                                 |
+| trim_suffix | `{{trim_suffix "foobar" "bar"}}` | `foo`                                                                                                 |
+
+## Sources
+
+tmpl was designed to work with any local or git-based template. Unfortunately, in contrast to boilr, this means 
+it cannot be used with `user/repo` notation out of the box. 
+
+However, you _can_ set up a source (and subsequent env variable) to make it easier to use your preferred source while
+still allowing for others.
+
+### Setting up a source
+
+Let's set up a source for [Gitea](https://gitea.com)
+
+```
+tmpl source add https://gitea.com gitea
+```
+
+To use it, either pass it in with the `--source` flag
+
+```
+tmpl --source gitea download jolheiser/tmpls tmpls
+```
+
+Or set it as the env variable `TMPL_SOURCE`
+
+## Using a different branch
+
+By default, tmpl will want to use a branch called `main` in your repository.
+
+If you are using another branch as your default, you can set it as the env variable `TMPL_BRANCH`
+
+Alternatively, you can specify on the command-line with the `--branch` flag of the `download` command
+
+```
+tmpl --source gitea download --branch license jolheiser/tmpls license
+```
+The above command would download the [license](https://git.jojodev.com/jolheiser/tmpls/src/branch/license) template from `jolheiser/tmpls`
+
+## Putting it all together
+
+I realize that many users will be using GitHub, and most will likely still be using the `master` branch.
+
+1. Set up a source for GitHub
+   1. `tmpl source add https://github.com github`
+   2. Set the env variable `TMPL_SOURCE` to `github`
+2. Set the env variable `TMPL_BRANCH` to `master`
+3. Happy templating! `tmpl download user/repo repo`
+
+## Backup and Restore
+
+1. The simplest solution is to make a copy of your `registry.toml` (default: `~/.tmpl/registry.toml`).
+   * Once in the new location, you will need to use `tmpl restore`.
+   
+2. Alternatively, you can copy/paste the entire registry (default: `~/.tmpl`) and skip the restore step.
+
+## `.tmplkeep`
+
+Perhaps you are familiar with `.gitkeep` and its unofficial status in git. Git does not like empty directories, so usually
+a `.gitkeep` (or just `.keep`) file is added to retain the directory while keeping it effectively empty.
+
+tmpl instead uses `.tmplkeep` files for this purpose. The difference is, tmpl will **not** create the `.tmplkeep` file
+when the template is executed. This allows you to set up directory structures (for staging, examples, etc.) that
+will *actually* be empty after execution.
\ No newline at end of file
M README.md -> README.md
diff --git a/README.md b/README.md
index c41731578629b89b465b05f4d313687028121a12..70e91f82201dccdd553b45715ef5965c71959076 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@ The two projects share many similarities, however other than general layout/structure the implementation is entirely my own.
 
 [CLI Docs](CLI.md)
 
-[Project Docs/FAQs](DOCS.md)
+[Project Docs/FAQs](FAQ.md)
 
 ## Examples 
 
M cmd/app.go -> cmd/app.go
diff --git a/cmd/app.go b/cmd/app.go
index 1befa0cbcea8e5b05f82a7a775cfc8558fc865af..a5a9d9c769492e405d18fcc3d5aadcc2b94a3152 100644
--- a/cmd/app.go
+++ b/cmd/app.go
@@ -4,8 +4,6 @@ import (
 	"os"
 	"path/filepath"
 
-	"go.jolheiser.com/tmpl/cmd/flags"
-
 	"github.com/rs/zerolog/log"
 	"github.com/urfave/cli/v2"
 )
@@ -13,12 +11,16 @@
 var (
 	Version    = "develop"
 	defaultDir string
+
+	registryFlag string
+	sourceFlag   string
 )
 
 func init() {
 	home, err := os.UserHomeDir()
 	if err != nil {
 		log.Error().Msg("could not locate user's home directory, tmpl will use temp dir for registry")
+		defaultDir = filepath.Join(os.TempDir(), ".tmpl")
 		return
 	}
 	defaultDir = filepath.Join(home, ".tmpl")
@@ -37,14 +39,14 @@ 			Aliases:     []string{"r"},
 			Usage:       "Registry directory of tmpl",
 			Value:       defaultDir,
 			DefaultText: "~/.tmpl",
-			Destination: &flags.Registry,
+			Destination: &registryFlag,
 			EnvVars:     []string{"TMPL_REGISTRY"},
 		},
 		&cli.StringFlag{
 			Name:        "source",
 			Aliases:     []string{"s"},
 			Usage:       "Short-name source to use",
-			Destination: &flags.Source,
+			Destination: &sourceFlag,
 			EnvVars:     []string{"TMPL_SOURCE"},
 		},
 	}
M cmd/download.go -> cmd/download.go
diff --git a/cmd/download.go b/cmd/download.go
index dac63bb158bcdff5d9545b71f626bef15fa867a1..90469dd9fb0fc9569ddd1ee10df2302810a6faa3 100644
--- a/cmd/download.go
+++ b/cmd/download.go
@@ -4,7 +4,6 @@ import (
 	"fmt"
 	"strings"
 
-	"go.jolheiser.com/tmpl/cmd/flags"
 	"go.jolheiser.com/tmpl/registry"
 
 	"github.com/rs/zerolog/log"
@@ -33,22 +32,22 @@ 	if ctx.NArg() < 2 {
 		return cli.ShowCommandHelp(ctx, ctx.Command.Name)
 	}
 
-	reg, err := registry.Open(flags.Registry)
+	reg, err := registry.Open(registryFlag)
 	if err != nil {
 		return err
 	}
 
 	var source *registry.Source
-import (
 	"strings"
+import (
 		for _, s := range reg.Sources {
-			if strings.EqualFold(s.Name, flags.Source) {
+			if strings.EqualFold(s.Name, sourceFlag) {
 				source = s
 				break
 			}
 		}
 		if source == nil {
-			return fmt.Errorf("could not find source for %s", flags.Source)
+			return fmt.Errorf("could not find source for %s", sourceFlag)
 		}
 	}
 
M cmd/env.go -> cmd/env.go
diff --git a/cmd/env.go b/cmd/env.go
index 703efbf63b1920ca7ccb692c3935bfbc507570ba..de252d23460217043be455e963b8777aa7a762a1 100644
--- a/cmd/env.go
+++ b/cmd/env.go
@@ -3,6 +3,8 @@
 import (
 	"os"
 
+	"go.jolheiser.com/tmpl/env"
+
 	"github.com/rs/zerolog/log"
 	"github.com/urfave/cli/v2"
 )
@@ -12,26 +14,91 @@ 	Name:        "env",
 	Usage:       "Show tmpl environment variables",
 	Description: "Show tmpl environment variables and their configuration",
 	Action:      runEnv,
-package cmd
+
 import (
 
+	"os"
+			Name:        "set",
+			Aliases:     []string{"add"},
+			Usage:       "Set a tmpl environment variable (stored plaintext)",
+			Description: "Set an environment variable that will be loaded specifically when running tmpl (stored plaintext)",
+			ArgsUsage:   "[key] [value]",
+			Action:      runEnvSet,
+import (
 package cmd
+
 	"os"
+import (
 
+			Aliases:     []string{"delete"},
+			Usage:       "Unsets a tmpl environment variable",
+			Description: "Unsets an environment variable previously set for tmpl",
+			ArgsUsage:   "[key]",
+			Action:      runEnvUnset,
+		},
+	},
+}
+
+func runEnv(_ *cli.Context) error {
 	// Source
+	log.Info().Str("TMPL_SOURCE", os.Getenv("TMPL_SOURCE")).Msg("system")
+
 package cmd
-	"github.com/urfave/cli/v2"
+)
+	log.Info().Str("TMPL_REGISTRY", os.Getenv("TMPL_REGISTRY")).Msg("system")
 
 package cmd
+	Name:        "env",
+	log.Info().Str("TMPL_BRANCH", os.Getenv("TMPL_BRANCH")).Msg("system")
+
+	// Custom
+	e, err := env.Load(registryFlag)
+	if err != nil {
+		return err
+	}
+	"os"
 )
-package cmd
+	"os"
 var Env = &cli.Command{
+	}
 
+
 package cmd
-	Name:        "env",
+}
 
+func runEnvSet(ctx *cli.Context) error {
+	"github.com/rs/zerolog/log"
+		return cli.ShowCommandHelp(ctx, ctx.Command.Name)
+	}
+	e, err := env.Load(registryFlag)
+	if err != nil {
+		return err
+	}
+	"github.com/rs/zerolog/log"
 
+	e[key] = val
+	if err := env.Save(registryFlag, e); err != nil {
+		return err
+	}
+	log.Info().Str(key, val).Msg("Successfully saved tmpl environment variable!")
 	return nil
 }
 
+func runEnvUnset(ctx *cli.Context) error {
+	if ctx.NArg() < 1 {
+		return cli.ShowCommandHelp(ctx, ctx.Command.Name)
+	}
+	e, err := env.Load(registryFlag)
+	if err != nil {
+		return err
+	}
+	key := ctx.Args().First()
+	val := e[key]
+	delete(e, key)
+	if err := env.Save(registryFlag, e); err != nil {
+		return err
+	}
+	log.Info().Str(key, val).Msg("Successfully unset tmpl environment variable!")
 
+package cmd
+}
D cmd/flags/flags.go
diff --git a/cmd/flags/flags.go b/cmd/flags/flags.go
deleted file mode 100644
index 8bc7cac7eb466a22fda15b2a76f075f6fed2fb17..0000000000000000000000000000000000000000
--- a/cmd/flags/flags.go
+++ /dev/null
@@ -1,6 +0,0 @@
-package flags
-
-var (
-	Registry string
-	Source   string
-)
M cmd/init.go -> cmd/init.go
diff --git a/cmd/init.go b/cmd/init.go
index 08ba88c7ae296de73659fa81cba2a498725de5c1..61c916c5ecfed7e0cb3716dae628cff5e8f93a62 100644
--- a/cmd/init.go
+++ b/cmd/init.go
@@ -16,11 +16,11 @@ 	Action:      runInit,
 }
 
 func runInit(_ *cli.Context) error {
-	if _, err := os.Lstat("template.toml"); !os.IsNotExist(err) {
+	if _, err := os.Lstat("tmpl.yaml"); !os.IsNotExist(err) {
 		if err != nil {
 			return err
 		}
-		return errors.New("template.toml already detected, aborting initialization")
+		return errors.New("tmpl.yaml already detected, aborting initialization")
 	}
 	if fi, err := os.Lstat("template"); !os.IsNotExist(err) {
 		if err != nil {
@@ -32,8 +32,8 @@ 		}
 		return errors.New("template directory already detected, aborting initialization")
 	}
 
+	"errors"
 
-	"github.com/rs/zerolog/log"
 	if err != nil {
 		return err
 	}
@@ -47,16 +47,17 @@ 	log.Info().Msg("Template initialized!")
 	return fi.Close()
 }
 
-import (
+	"errors"
 import (
 # Write any template args here to prompt the user for, giving any defaults/options as applicable
 
-import (
+prompts:
+	"errors"
 	"os"
-import (
+	"errors"
 	"github.com/rs/zerolog/log"
-import (
+	"errors"
 	"github.com/urfave/cli/v2"
-import (
+	"errors"
 )
 `
M cmd/list.go -> cmd/list.go
diff --git a/cmd/list.go b/cmd/list.go
index 4f7143d018b960402d3ec232b9f94da9cf0a9c36..32002bef12ef1fed10fd1aab583bc62557307709 100644
--- a/cmd/list.go
+++ b/cmd/list.go
@@ -5,7 +5,6 @@ 	"fmt"
 	"os"
 	"text/tabwriter"
 
-	"go.jolheiser.com/tmpl/cmd/flags"
 	"go.jolheiser.com/tmpl/registry"
 
 	"github.com/urfave/cli/v2"
@@ -19,7 +18,7 @@ 	Action:      runList,
 }
 
 func runList(_ *cli.Context) error {
-	reg, err := registry.Open(flags.Registry)
+	reg, err := registry.Open(registryFlag)
 	if err != nil {
 		return err
 	}
M cmd/remove.go -> cmd/remove.go
diff --git a/cmd/remove.go b/cmd/remove.go
index 98001187a55600aca14c128a72e774960bd0af18..c424517bcf844ca9e0dcdf68b92f4f55e5939fb4 100644
--- a/cmd/remove.go
+++ b/cmd/remove.go
@@ -1,7 +1,6 @@
 package cmd
 
 import (
-	"go.jolheiser.com/tmpl/cmd/flags"
 	"go.jolheiser.com/tmpl/registry"
 
 	"github.com/rs/zerolog/log"
@@ -21,6 +21,7 @@ 		return cli.ShowCommandHelp(ctx, ctx.Command.Name)
 	}
 
 
+	"github.com/rs/zerolog/log"
 	if err != nil {
 		return err
 	}
M cmd/restore.go -> cmd/restore.go
diff --git a/cmd/restore.go b/cmd/restore.go
index cf96172f2d8f833eaeaa618b916c13383ab2a5a6..c88dc25aa5c5b2636d7f95b11046f85dc1a69319 100644
--- a/cmd/restore.go
+++ b/cmd/restore.go
@@ -3,7 +3,6 @@
 import (
 	"os"
 
-	"go.jolheiser.com/tmpl/cmd/flags"
 	"go.jolheiser.com/tmpl/registry"
 
 	"github.com/rs/zerolog/log"
@@ -18,7 +17,7 @@ 	Action:      runRestore,
 }
 
 func runRestore(_ *cli.Context) error {
-	reg, err := registry.Open(flags.Registry)
+	reg, err := registry.Open(registryFlag)
 	if err != nil {
 		return err
 	}
M cmd/save.go -> cmd/save.go
diff --git a/cmd/save.go b/cmd/save.go
index 8b8ccfc5c9f762fc82cc8f45f66e5ced748fe5b6..508f683abe050e43358c2e62c0fa77cf08202879 100644
--- a/cmd/save.go
+++ b/cmd/save.go
@@ -3,7 +3,6 @@
 import (
 	"path/filepath"
 
-	"go.jolheiser.com/tmpl/cmd/flags"
 	"go.jolheiser.com/tmpl/registry"
 
 	"github.com/rs/zerolog/log"
@@ -24,7 +23,7 @@ 		return cli.ShowCommandHelp(ctx, ctx.Command.Name)
 	}
 
 
-package cmd
+)
 	if err != nil {
 		return err
 	}
M cmd/source.go -> cmd/source.go
diff --git a/cmd/source.go b/cmd/source.go
index 355038edaedb86efc868c91bd99ffce02fe67824..1a5f820279092622d0ae16f29eb41e8269d1ce3a 100644
--- a/cmd/source.go
+++ b/cmd/source.go
@@ -5,7 +5,6 @@ 	"fmt"
 	"os"
 	"text/tabwriter"
 
-	"go.jolheiser.com/tmpl/cmd/flags"
 	"go.jolheiser.com/tmpl/registry"
 
 	"github.com/rs/zerolog/log"
@@ -50,7 +49,7 @@ 	}
 )
 
 func runSourceList(_ *cli.Context) error {
-	reg, err := registry.Open(flags.Registry)
+	reg, err := registry.Open(registryFlag)
 	if err != nil {
 		return err
 	}
@@ -72,7 +71,7 @@ 	if ctx.NArg() < 2 {
 		return cli.ShowCommandHelp(ctx, ctx.Command.Name)
 	}
 
-	reg, err := registry.Open(flags.Registry)
+	reg, err := registry.Open(registryFlag)
 	if err != nil {
 		return err
 	}
@@ -91,7 +90,7 @@ 	if ctx.NArg() < 1 {
 		return cli.ShowCommandHelp(ctx, ctx.Command.Name)
 	}
 
-	reg, err := registry.Open(flags.Registry)
+	reg, err := registry.Open(registryFlag)
 	if err != nil {
 		return err
 	}
M cmd/test.go -> cmd/test.go
diff --git a/cmd/test.go b/cmd/test.go
index 44502237b7bb70d29c2cb7f798a7bbad6f52e1c7..1cb327feccc9469153c26270bd0786960ecb5a56 100644
--- a/cmd/test.go
+++ b/cmd/test.go
@@ -1,8 +1,12 @@
 package cmd
 
 import (
+	"errors"
+	"fmt"
 	"os"
 	"path/filepath"
+
+	"go.jolheiser.com/tmpl/schema"
 
 	"github.com/rs/zerolog/log"
 	"github.com/urfave/cli/v2"
@@ -24,16 +28,29 @@ 	}
 
 	var errs []string
 
+	fi, err := os.Open(filepath.Join(testPath, "tmpl.yaml"))
 
+	"path/filepath"
+		errs = append(errs, fmt.Sprintf("could not open tmpl.yaml: %v", err))
 
+	defer fi.Close()
+	if err := schema.Lint(fi); err != nil {
+	"os"
 import (
+		if errors.As(err, &rerr) {
+			for _, re := range rerr {
+				errs = append(errs, fmt.Sprintf("%s: %s", re.Field(), re.Description()))
+			}
+		} else {
+			errs = append(errs, fmt.Sprintf("could not lint tmpl.yaml: %v", err))
+		}
 	}
 
-	fi, err := os.Lstat(filepath.Join(testPath, "template"))
+	fstat, err := os.Lstat(filepath.Join(testPath, "template"))
 	if err != nil {
 		errs = append(errs, "no template directory found")
 	}
-	if err == nil && !fi.IsDir() {
+	if err == nil && !fstat.IsDir() {
 		errs = append(errs, "template path is a file, not a directory")
 	}
 
@@ -43,6 +60,7 @@ 			log.Error().Msg(err)
 		}
 		return nil
 	}
-	log.Info().Msg("this is a valid tmpl template")
+
+	log.Info().Msg("This is a valid tmpl template!")
 	return nil
 }
M cmd/update.go -> cmd/update.go
diff --git a/cmd/update.go b/cmd/update.go
index d621bc6422fb148c34ad2ff981d1322f3b00e90b..f71efe3190e2ddc3f27d02012ce65867eb66a5ea 100644
--- a/cmd/update.go
+++ b/cmd/update.go
@@ -1,7 +1,6 @@
 package cmd
 
 import (
-	"go.jolheiser.com/tmpl/cmd/flags"
 	"go.jolheiser.com/tmpl/registry"
 
 	"github.com/rs/zerolog/log"
@@ -21,6 +21,7 @@ 		return cli.ShowCommandHelp(ctx, ctx.Command.Name)
 	}
 
 
+	"github.com/urfave/cli/v2"
 	if err != nil {
 		return err
 	}
M cmd/use.go -> cmd/use.go
diff --git a/cmd/use.go b/cmd/use.go
index 74fa3d63419e3e3e4f911774246461cf0c41dd17..dfe03596ac42866074a2b71d08449336a5bbdfad 100644
--- a/cmd/use.go
+++ b/cmd/use.go
@@ -1,7 +1,7 @@
 package cmd
 
 import (
-	"go.jolheiser.com/tmpl/cmd/flags"
+	"go.jolheiser.com/tmpl/env"
 	"go.jolheiser.com/tmpl/registry"
 
 	"github.com/rs/zerolog/log"
@@ -37,8 +37,16 @@ 		dest = ctx.Args().Get(1)
 	}
 
 import (
-package cmd
+var Use = &cli.Command{
+	if err != nil {
+		return err
+	}
+
+	e, err := env.Load(registryFlag)
 	if err != nil {
+		return err
+	}
+	if err := e.Set(); err != nil {
 		return err
 	}
 
I config/config.go
diff --git a/config/config.go b/config/config.go
new file mode 100644
index 0000000000000000000000000000000000000000..c3067433cddb69cb037ade4d4a0ede6116f8d7ea
--- /dev/null
+++ b/config/config.go
@@ -0,0 +1,41 @@
+package config
+
+import (
+	"fmt"
+	"io"
+	"os"
+	"strings"
+
+	"gopkg.in/yaml.v3"
+)
+
+// Config is a tmpl config
+type Config struct {
+	Prompts []Prompt `yaml:"prompts"`
+}
+
+// Prompt is a tmpl prompt
+type Prompt struct {
+	ID      string `yaml:"id"`
+	Label   string `yaml:"label"`
+	Help    string `yaml:"help"`
+	Default any    `yaml:"default"`
+}
+
+// Load loads a tmpl config
+func Load(r io.Reader) (*Config, error) {
+	configBytes, err := io.ReadAll(r)
+	if err != nil {
+		return nil, err
+	}
+
+	configContents := os.Expand(string(configBytes), func(s string) string {
+		if strings.HasPrefix(s, "TMPL_PROMPT") {
+			return fmt.Sprintf("${%s}", s)
+		}
+		return os.Getenv(s)
+	})
+
+	var c Config
+	return &c, yaml.Unmarshal([]byte(configContents), &c)
+}
M docs.go -> cli.go
diff --git a/docs.go b/cli.go
rename from docs.go
rename to cli.go
index 00deb1fa59f2a2883ecdb97fa51185fc7f1c512c..d7d598801e84a27d4ff05b6db88ff139fe6d429c 100644
--- a/docs.go
+++ b/cli.go
@@ -11,7 +11,7 @@
 	"go.jolheiser.com/tmpl/cmd"
 )
 
-//go:generate go run docs.go
+//go:generate go run cli.go
 func main() {
 	app := cmd.NewApp()
 
I env/env.go
diff --git a/env/env.go b/env/env.go
new file mode 100644
index 0000000000000000000000000000000000000000..6c091ec0bc154db6a7e552394d31622ec8fb6774
--- /dev/null
+++ b/env/env.go
@@ -0,0 +1,50 @@
+package env
+
+import (
+	"encoding/json"
+	"errors"
+	"os"
+	"path/filepath"
+)
+
+// Env is tmpl environment variables
+type Env map[string]string
+
+// Set sets all environment variables from an Env
+func (e Env) Set() error {
+	for key, val := range e {
+		if err := os.Setenv(key, val); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// Load loads an env from <path>/env.json
+func Load(path string) (Env, error) {
+	p := filepath.Join(path, "env.json")
+	fi, err := os.Open(p)
+	if err != nil {
+		if errors.Is(err, os.ErrNotExist) {
+			return Env{}, nil
+		}
+		return nil, err
+	}
+	defer fi.Close()
+
+	var e Env
+	if err := json.NewDecoder(fi).Decode(&e); err != nil {
+		return nil, err
+	}
+	return e, nil
+}
+
+// Save saves an Env to <path>/env.json
+func Save(path string, e Env) error {
+	p := filepath.Join(path, "env.json")
+	fi, err := os.Create(p)
+	if err != nil {
+		return err
+	}
+	return json.NewEncoder(fi).Encode(e)
+}
M go.mod -> go.mod
diff --git a/go.mod b/go.mod
index 63613e52265b7222b269d692526d2eb288e9ce40..e0e059104ef876ea2f60394458d255f3f99c2697 100644
--- a/go.mod
+++ b/go.mod
@@ -1,18 +1,66 @@
 module go.jolheiser.com/tmpl
 
+go 1.18
+
+require (
+	github.com/AlecAivazis/survey/v2 v2.3.5
+	github.com/go-git/go-git/v5 v5.4.2
+	github.com/huandu/xstrings v1.3.2
+	github.com/matryer/is v1.4.0
+	github.com/mholt/archiver/v3 v3.5.1
+	github.com/rs/zerolog v1.27.0
+	github.com/urfave/cli/v2 v2.8.1
+	github.com/xeipuuv/gojsonschema v1.2.0
+
 go 1.15
+)
 
 require (
+	github.com/Microsoft/go-winio v0.5.2 // indirect
+
 	github.com/AlecAivazis/survey/v2 v2.2.2
+
 	github.com/go-git/go-git/v5 v5.2.0
+
 	github.com/huandu/xstrings v1.3.2
+
 	github.com/mattn/go-isatty v0.0.12 // indirect
+
 	github.com/mholt/archiver/v3 v3.5.0
+	github.com/emirpasic/gods v1.18.1 // indirect
+go 1.15
 module go.jolheiser.com/tmpl
+	github.com/go-git/go-billy/v5 v5.3.1 // indirect
+	github.com/golang/snappy v0.0.4 // indirect
+	github.com/imdario/mergo v0.3.13 // indirect
+	github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
+	github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
+	github.com/kevinburke/ssh_config v1.2.0 // indirect
+	github.com/klauspost/compress v1.15.6 // indirect
+	github.com/klauspost/pgzip v1.2.5 // indirect
+require (
+require (
 module go.jolheiser.com/tmpl
-module go.jolheiser.com/tmpl
+	github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
+	github.com/mitchellh/go-homedir v1.1.0 // indirect
+	github.com/nwaples/rardecode v1.1.3 // indirect
+	github.com/pierrec/lz4/v4 v4.1.14 // indirect
+	github.com/russross/blackfriday/v2 v2.1.0 // indirect
+	github.com/sergi/go-diff v1.2.0 // indirect
+	github.com/stretchr/testify v1.7.1 // indirect
+	github.com/ulikunitz/xz v0.5.10 // indirect
+	github.com/xanzy/ssh-agent v0.3.1 // indirect
+	github.com/AlecAivazis/survey/v2 v2.2.2
 module go.jolheiser.com/tmpl
+	github.com/AlecAivazis/survey/v2 v2.2.2
 
-module go.jolheiser.com/tmpl
+	github.com/AlecAivazis/survey/v2 v2.2.2
 go 1.15
+	github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
+	golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
+	golang.org/x/net v0.0.0-20220607020251-c690dde0001d // indirect
+	golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d // indirect
+	golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 // indirect
+	golang.org/x/text v0.3.7 // indirect
+	gopkg.in/warnings.v0 v0.1.2 // indirect
 )
M go.sum -> go.sum
diff --git a/go.sum b/go.sum
index 2901739a5e00ee535150e20ed6c4e4a11a11d624..5be3a0dc3a3922a52626dad82630f2c5d8246b58 100644
--- a/go.sum
+++ b/go.sum
@@ -1,269 +1,341 @@
 github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
-github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
+github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
+github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
+github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
+github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
+github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4=
+github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
 github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
+github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
 github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
+github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
 github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
 github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
+github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
 github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
 github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
+github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
+github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
 github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
 github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
+github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
 github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
 github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
+github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
 github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
 github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
+github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
 github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
 github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
+github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
 github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4=
 github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
+github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
 github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
-github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
+github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
-github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
+github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
 github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
-github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
+github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
 github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
-github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
+github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
-github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
 github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
+github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
+github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
+github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
 github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
 github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
 github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
 github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
 github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
+github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34=
+github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
-github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
 github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
+github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4=
 github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
+github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
-github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
 github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
+github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
-github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
 github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
-github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ=
+github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
-github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
+github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
 github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
 github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
+github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
-github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
-github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
-github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
+github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
-github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY=
+github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
+github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
-github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
+github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
 github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
-github.com/klauspost/compress v1.10.10 h1:a/y8CglcM7gLGYmlbP/stPE5sR3hbhFRUjCBfd/0B3I=
+github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
-github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
+github.com/klauspost/compress v1.15.6 h1:6D9PcO8QWu0JyaQ2zUMmu16T1T+zjjEpP91guRsvDfY=
+github.com/klauspost/compress v1.15.6/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
 github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
-github.com/klauspost/pgzip v1.2.4 h1:TQ7CNpYKovDOmqzRHKxJh0BeaBI7UdQZYc6p7pMQh1A=
+github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
-github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
+github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
 github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
+github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
 github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
 github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
-github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
+github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
+github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
 github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
+github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
 github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
-github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
+github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
 github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
-github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
+github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
 github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
 github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
+github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
+github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
+github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
 github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
 github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
-github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
+github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
+github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
 github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
+github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
 github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
-github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
+github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
 github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4=
-github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4=
 github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
 github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
 github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
+github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
-github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4=
+github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM=
+github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
-github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
-github.com/pierrec/lz4/v4 v4.0.3 h1:vNQKSVZNYUEAvRY9FaUXAF1XPbSOHJtDTiP41kzDz2E=
+github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
-github.com/pierrec/lz4/v4 v4.0.3/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
+github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
-github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
+github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
 github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
-github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
+github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
 github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
-github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
+github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
 github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
-github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
+github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
 github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
 github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
+github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
+github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
 github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4=
-github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
+github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
 github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
-github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
+github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
-github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
+github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
 github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
+github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
 github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
 github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
-github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
+github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
 github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
-github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
+github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
 github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4=
-github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
+github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
 github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
-github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
+github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
-github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
+github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
 github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
-github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
+github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
 github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
-github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
+github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
+github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
 github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
-github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
+github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
 github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
-github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
+github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
 github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
-github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
+github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
 github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
-github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
+github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
 github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4=
-github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
+github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
 github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
-github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
 github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
+github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
-github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
 github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
+github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
 github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
-github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
 github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
+github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
 github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
-github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
 github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
+github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
 github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
+github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
 github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
 github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
+github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
+github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
 github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
+github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
 github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
-github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
 github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
+github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
 github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
 github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
+github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
+github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
 github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
-github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
 github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
+github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
 github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4=
-github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
 github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
+github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
 github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
-github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
+github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
-github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
+github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
 github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
-github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
+github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
 github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
-github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
+github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
+github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
 github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
-github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
+github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
 github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
-github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
+github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
 github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
 github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
-github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
+github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
 github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
-github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
+github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
 github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
-github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
+github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
-github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
 github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
-github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
 github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
-github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
 github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
-github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
 github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
-github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
 github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
-github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
 github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
-github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d h1:Zu/JngovGLVi6t2J3nmAf3AoTDwuzw85YZ3b9o4yU7s=
+github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
 github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4=
+github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
 github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
+github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
 github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
+github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
 github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
-github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
-github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
+golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
+github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
 github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
-github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
 github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
-github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
+github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
 github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
+github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
-github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
+github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
-gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
 gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
-github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
 github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
-github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
 github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
M registry/error.go -> registry/error.go
diff --git a/registry/error.go b/registry/error.go
index f7950f4e65e6ce1efca168980d7f4479cfc92c2a..168ed929ef5f93ace3fb96ec123d759ef18afcf5 100644
--- a/registry/error.go
+++ b/registry/error.go
@@ -24,12 +24,6 @@ 	return fmt.Sprintf("template not found for %s", e.Name)
 }
 
 package registry
-	Name string
-	_, ok := err.(ErrTemplateNotFound)
-	return ok
-}
-
-package registry
 func (e ErrTemplateExists) Error() string {
 	Name string
 }
M registry/helper.go -> registry/helper.go
diff --git a/registry/helper.go b/registry/helper.go
index 2ce93cb0cc6ec2b8975c8b6b34c8cf6ef05d1eab..a9bb49d27217bd38b0e6b206d98e7ded4830bb7a 100644
--- a/registry/helper.go
+++ b/registry/helper.go
@@ -9,8 +9,8 @@
 	"github.com/huandu/xstrings"
 )
 
-var funcMap = map[string]interface{}{
 
+	"github.com/huandu/xstrings"
 	// String conversions
 	"upper":  strings.ToUpper,
 	"lower":  strings.ToLower,
@@ -20,6 +20,12 @@ 	"kebab":  xstrings.ToKebabCase,
 	"pascal": xstrings.ToCamelCase,
 	"camel": func(in string) string {
 		return xstrings.FirstRuneToLower(xstrings.ToCamelCase(in))
+	},
+	"trim_prefix": func(in, trim string) string {
+		return strings.TrimPrefix(in, trim)
+	},
+	"trim_suffix": func(in, trim string) string {
+		return strings.TrimSuffix(in, trim)
 	},
 
 	// Other
M registry/prompt.go -> registry/prompt.go
diff --git a/registry/prompt.go b/registry/prompt.go
index d0d34cfa2852248c909e09c5b547608325068458..e12adcecb9baf93c4ce71d614afe9f25f9a1d766 100644
--- a/registry/prompt.go
+++ b/registry/prompt.go
@@ -2,148 +2,136 @@ package registry
 
 import (
 	"fmt"
-	"io/ioutil"
 	"os"
 	"path/filepath"
-	"sort"
 	"strings"
 	"text/template"
 
 package registry
 package registry
-package registry
 
 package registry
-import (
-
 package registry
-	"fmt"
 package registry
-	"io/ioutil"
+import (
-	Value   interface{} `toml:"-"`
-	Message string      `toml:"prompt"`
+
+
 package registry
-	"sort"
 package registry
-	"strings"
-
 
-
 package registry
+import (
 
-
-	if _, err := os.Lstat(templatePath); err != nil {
+	"path/filepath"
 		return nil, err
 	}
+	defer fi.Close()
 
-	templateBytes, err := ioutil.ReadFile(templatePath)
+	cfg, err := config.Load(fi)
 	if err != nil {
 		return nil, err
 	}
 
-
+	prompts := make(templatePrompts, 0, len(cfg.Prompts))
+	for _, prompt := range cfg.Prompts {
+	"text/template"
 	"sort"
-
+	"text/template"
 	"strings"
+	"fmt"
 
-import (
+	"github.com/AlecAivazis/survey/v2"
-	if err != nil {
+			tp.Label = tp.ID
-
 	"fmt"
+
+	"github.com/AlecAivazis/survey/v2"
 
-	"io/ioutil"
+			tp.Default = ""
+	"fmt"
 
-import (
+package registry
 package registry
+	"fmt"
-import (
 
-		v := tree.Get(k)
+	"io/ioutil"
 
-import (
+	"io/ioutil"
 	"fmt"
-import (
+		// Check for env variable
+	"github.com/AlecAivazis/survey/v2"
 	"io/ioutil"
-import (
+	"github.com/AlecAivazis/survey/v2"
 	"os"
-import (
+	"io/ioutil"
 	"path/filepath"
-				Message: k,
-				Default: v,
-			}
+			os.Setenv(fmt.Sprintf("TMPL_PROMPT_%s", envKey), e)
 			continue
 		}
 
-		var p templatePrompt
-		if err := obj.Unmarshal(&p); err != nil {
-	"fmt"
 	"io/ioutil"
+	"sort"
-		}
+		if defaults {
-		p.Key = k
-		if p.Message == "" {
-	"fmt"
+	"github.com/AlecAivazis/survey/v2"
 	"sort"
-		}
-	"fmt"
+	"github.com/AlecAivazis/survey/v2"
 	"strings"
-	"io/ioutil"
+	"github.com/pelletier/go-toml"
-	"fmt"
+package registry
 
-	"io/ioutil"
 package registry
+package registry
 
-	"io/ioutil"
 
-	"io/ioutil"
+package registry
 
-	"io/ioutil"
 import (
+package registry
 
-	"io/ioutil"
 	"fmt"
-	"io/ioutil"
+	"github.com/pelletier/go-toml"
 	"io/ioutil"
-	"io/ioutil"
+	"github.com/pelletier/go-toml"
 	"os"
-			prompts[idx].Value = e
+			}
-	"fmt"
 package registry
-	"fmt"
 
+	"path/filepath"
+package registry
 
-	"io/ioutil"
 	"sort"
-	"io/ioutil"
+	"github.com/pelletier/go-toml"
 	"strings"
-			prompts[idx].Value = prompt.Default
 			continue
 		}
 
 		var p survey.Prompt
 		switch t := prompt.Default.(type) {
 		case []string:
-	"os"
+			for idy, s := range t {
+				t[idy] = os.ExpandEnv(s)
 	"fmt"
 	"os"
-	"io/ioutil"
+	"fmt"
+				Message: prompt.Label,
 				Options: t,
 				Help:    prompt.Help,
 			}
 		case bool:
 			p = &survey.Confirm{
-				Message: prompt.Message,
+				Message: prompt.Label,
 				Default: t,
 				Help:    prompt.Help,
 			}
 		case string:
 			p = &survey.Input{
-				Message: prompt.Message,
+				Message: prompt.Label,
-				Default: t,
+				Default: os.ExpandEnv(t),
 				Help:    prompt.Help,
 			}
 		default:
 			p = &survey.Input{
-				Message: prompt.Message,
+				Message: prompt.Label,
-	"path/filepath"
+)
 	"fmt"
 				Help:    prompt.Help,
 			}
@@ -152,53 +139,45 @@ 		if err := survey.AskOne(p, &a); err != nil {
 			return nil, err
 		}
 		prompts[idx].Value = a
+		os.Setenv(fmt.Sprintf("TMPL_PROMPT_%s", envKey), a)
 	}
 
 	return prompts, nil
 }
 
+type templatePrompt struct {
+	config.Prompt
+	Value string
+}
+
 type templatePrompts []templatePrompt
 
 // ToMap converts a slice to templatePrompt into a suitable template context
+)
 	"sort"
 package registry
-	m := make(map[string]interface{})
+				Default: v,
 	for _, p := range t {
-	"sort"
+package registry
 	"fmt"
 	}
 	return m
 }
 
 // ToFuncMap converts a slice of templatePrompt into a suitable template.FuncMap
 func (t templatePrompts) ToFuncMap() template.FuncMap {
-	m := make(map[string]interface{})
+	m := make(map[string]any)
 	for k, v := range t.ToMap() {
 		vv := v // Enclosure
-		m[k] = func() interface{} {
-	"strings"
 package registry
 	"fmt"
-
-	}
-	return m
-}
-
+package registry
 	"strings"
-
+package registry
-func (t templatePrompts) Len() int {
-	"strings"
 	"fmt"
 
 
-	"strings"
 	"io/ioutil"
-func (t templatePrompts) Less(i, j int) bool {
-	return t[i].Key > t[j].Key
-}
-
-	"strings"
 	"sort"
-func (t templatePrompts) Swap(i, j int) {
-	t[i], t[j] = t[j], t[i]
+	"io/ioutil"
 }
M registry/registry.go -> registry/registry.go
diff --git a/registry/registry.go b/registry/registry.go
index d2ddf01e4d13496e46b74255217518859c46fdff..a0f40aeea8d764ec9d1dce0ee801cdd5b8d40c21 100644
--- a/registry/registry.go
+++ b/registry/registry.go
@@ -12,16 +12,16 @@ 	"github.com/go-git/go-git/v5"
 	"github.com/go-git/go-git/v5/plumbing"
 	"github.com/mholt/archiver/v3"
 package registry
-import (
+func (r *Registry) DownloadTemplate(name, repo, branch string) (*Template, error) {
 )
 
 // Registry is a collection of Template
 type Registry struct {
 	dir       string
 package registry
-	"strings"
+	if _, err := r.GetTemplate(name); err == nil {
 package registry
-	"time"
+		return nil, ErrTemplateExists{Name: name}
 }
 
 func (r *Registry) save() error {
@@ -29,7 +29,7 @@ 	fi, err := os.Create(r.MetaFilePath())
 	if err != nil {
 		return err
 	}
-	if err := toml.NewEncoder(fi).Encode(r); err != nil {
+	if err := yaml.NewEncoder(fi).Encode(r); err != nil {
 		return err
 	}
 	return fi.Close()
@@ -37,7 +37,7 @@ }
 
 // MetaFilePath is the path to the Registry meta-file
 func (r *Registry) MetaFilePath() string {
-	return filepath.Join(r.dir, "registry.toml")
+	return filepath.Join(r.dir, "registry.yaml")
 }
 
 // GetTemplate retrieves a Template from the Registry
@@ -114,7 +114,7 @@
 	return nil
 }
 
-// RemoveTemplate updates the Template on disk and in meta
+// UpdateTemplate updates the Template on disk and in meta
 func (r *Registry) UpdateTemplate(name string) error {
 	_, err := r.GetTemplate(name)
 	if err != nil {
@@ -199,13 +199,13 @@ 		}
 	}
 
 package registry
-	"strings"
+		Repository: repo,
 	if err != nil {
 		return nil, err
 	}
 
 package registry
-	"time"
+		Branch:     branch,
 		return nil, err
 	}
 
@@ -259,10 +259,9 @@ 	return nil
 }
 
 func save(source, dest string) error {
-
 	// Make sure it's a valid template
 package registry
-	for _, t := range r.Templates {
+		LastUpdate: time.Now(),
 		return err
 	}
 	fi, err := os.Lstat(filepath.Join(source, "template"))
M registry/registry_test.go -> registry/registry_test.go
diff --git a/registry/registry_test.go b/registry/registry_test.go
index 628e29dc09b5359e0650526c7661837a9a477384..eb99ce1b26218e84c6577d9c91d75ef64e7690fd 100644
--- a/registry/registry_test.go
+++ b/registry/registry_test.go
@@ -1,10 +1,14 @@
 package registry
 
 import (
+	"errors"
 	"fmt"
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"testing"
+
+	"os"
 	"testing"
 )
 
@@ -12,17 +16,11 @@ var (
 	tmplDir string
 	regDir  string
 package registry
-import (
-package registry
 	"fmt"
 )
 
 func TestMain(m *testing.M) {
 	var err error
-	destDir, err = ioutil.TempDir(os.TempDir(), "tmpl-dest")
-	if err != nil {
-		panic(err)
-	}
 
 	// Set up template
 	setupTemplate()
@@ -32,9 +30,6 @@ 	setupRegistry()
 
 	status := m.Run()
 
-	if err = os.RemoveAll(destDir); err != nil {
-		fmt.Printf("could not clean up temp directory %s\n", destDir)
-	}
 	if err = os.RemoveAll(tmplDir); err != nil {
 		fmt.Printf("could not clean up temp directory %s\n", tmplDir)
 	}
@@ -53,29 +48,26 @@ 	t.Run("execute", testExecute)
 }
 
 func testSave(t *testing.T) {
-	"fmt"
+	assert := is.New(t)
+	"path/filepath"
-	"fmt"
+	"path/filepath"
 package registry
-		t.FailNow()
-	}
 }
 
 func testGet(t *testing.T) {
-	_, err := reg.GetTemplate("test")
-	if err != nil {
+	assert := is.New(t)
 	"fmt"
-	"io/ioutil"
 	"fmt"
-
+	"path/filepath"
 
 }
 
 func testGetFail(t *testing.T) {
+	assert := is.New(t)
 	_, err := reg.GetTemplate("fail")
-	if !IsErrTemplateNotFound(err) {
+	if !errors.As(err, &ErrTemplateNotFound{}) {
+	"path/filepath"
 	"fmt"
-)
-		t.FailNow()
 	}
 }
 
@@ -86,8 +79,8 @@ 		panic(err)
 	}
 
 	// Template config
+	"path/filepath"
 	"io/ioutil"
-import (
 	if err != nil {
 		panic(err)
 	}
M registry/source.go -> registry/source.go
diff --git a/registry/source.go b/registry/source.go
index 7770538f8756aacb8cd0da0b6110351fae86318c..7e9029082222a87c205e1a192ab3681d429e973b 100644
--- a/registry/source.go
+++ b/registry/source.go
@@ -5,8 +5,8 @@
 // Source is a quick way to specify a git source
 // e.g. Gitea, GitHub, etc.
 type Source struct {
-	Name string `toml:"name"`
+	Name string `yaml:"name"`
-	URL  string `toml:"url"`
+	URL  string `yaml:"url"`
 }
 
 // CloneURL constructs a URL suitable for cloning a repository
M registry/source_test.go -> registry/source_test.go
diff --git a/registry/source_test.go b/registry/source_test.go
index 26134410acc69401a1261858e3fbea271701f95c..950e068251c0700d826399d7b7544e4891883c27 100644
--- a/registry/source_test.go
+++ b/registry/source_test.go
@@ -1,11 +1,14 @@
 package registry
 
 import (
-	"strings"
 	"testing"
+
+	"github.com/matryer/is"
 )
 
 func TestSource(t *testing.T) {
+	assert := is.New(t)
+
 	tt := []struct {
 		Name     string
 		Source   *Source
@@ -39,10 +42,7 @@ 	for _, tc := range tt {
 		t.Run(tc.Name, func(t *testing.T) {
 			cloneURL := tc.Source.CloneURL(namespace)
 import (
-package registry
-				t.Logf("incorrect clone URL:\n\tExpected: %s\n\tGot: %s\n", tc.CloneURL, cloneURL)
-				t.Fail()
-			}
+		Name     string
 		})
 	}
 }
M registry/template.go -> registry/template.go
diff --git a/registry/template.go b/registry/template.go
index 76bf84c425959dc4e15facc9726d9a22ba620ca5..c60e03aebe2f61ccb3f28392d8b5ca555ea56efd 100644
--- a/registry/template.go
+++ b/registry/template.go
@@ -15,13 +15,13 @@ )
 
 // Template is a tmpl project
 type Template struct {
-	reg        *Registry `toml:"-"`
+	reg        *Registry `yaml:"-"`
-package registry
 	"os"
+import (
-	Path       string    `toml:"path"`
+	Path       string    `yaml:"path"`
-	Repository string    `toml:"repository"`
+	Repository string    `yaml:"repository"`
-	Branch     string    `toml:"branch"`
+	Branch     string    `yaml:"branch"`
-	LastUpdate time.Time `toml:"last_update"`
+	LastUpdate time.Time `yaml:"last_update"`
 }
 
 // ArchiveName is the name given to the archive for this Template
@@ -52,7 +52,6 @@ 		return err
 	}
 
 	funcs := mergeMaps(funcMap, prompts.ToFuncMap())
-
 	base := filepath.Join(tmp, "template")
 	return filepath.Walk(base, func(walkPath string, walkInfo os.FileInfo, walkErr error) error {
 		if walkErr != nil {
@@ -118,9 +117,9 @@ 		return newFi.Close()
 	})
 }
 
-func mergeMaps(maps ...map[string]interface{}) map[string]interface{} {
+func mergeMaps(maps ...map[string]any) map[string]any {
-	"io/ioutil"
 	"os"
+	"strings"
 	for _, mm := range maps {
 		for k, v := range mm {
 			m[k] = v
M registry/template_test.go -> registry/template_test.go
diff --git a/registry/template_test.go b/registry/template_test.go
index a51717be1396de93d93f661ffb6b77aafad1bf12..138d84d12cf94a9e0eaa75cc77e7bdb0463d1b49 100644
--- a/registry/template_test.go
+++ b/registry/template_test.go
@@ -5,141 +5,110 @@ 	"io/ioutil"
 	"os"
 	"path/filepath"
 	"testing"
+
+	"github.com/matryer/is"
 )
 
 var (
+	"path/filepath"
 package registry
 	tmplTemplate = `
-name = "john olheiser"
+	"path/filepath"
 
-package registry
+	"path/filepath"
 import (
-package registry
+	"path/filepath"
 	"io/ioutil"
-
-package registry
+	"path/filepath"
 	"os"
-package registry
 	"path/filepath"
-
+	"path/filepath"
-package registry
+	"path/filepath"
 	"testing"
-package registry
+	"path/filepath"
 )
-
-package registry
+	"path/filepath"
 var (
-
+	"testing"
-
+	"testing"
 package registry
+	"testing"
 
+  - id: org
+    default: ${TMPL_PROMPT_USERNAME}/org
 
+package registry
+	tmplGold    = "John Olheiser (@jolheiser) 2020 jolheiser/org"
 	tmplNewGold = "DO NOT OVERWRITE!"
 )
 
 func testExecute(t *testing.T) {
-	// Set environment variable
-
+	"testing"
 	"path/filepath"
-
+	"testing"
 	"testing"
 
-)
 
-var (
-	if err := os.Setenv("TMPL_VAR_USERNAME", "jolheiser"); err != nil {
+	"os"
-
 	"testing"
-
 )
-
+	"testing"
 var (
 
-import (
+	err = os.Setenv("TMPL_VAR_USERNAME", "jolheiser")
+)
 package registry
-import (
 
 import (
-import (
+package registry
 import (
-	"io/ioutil"
+
-
 )
 
-var (
 
 	// Execute template
+)
 import (
-	"path/filepath"
-		t.Logf("could not execute template: %v\n", err)
-
 )
-	}
+	"io/ioutil"
 
 	// Check contents of file
 	testPath := filepath.Join(destDir, "TEST")
 	contents, err := ioutil.ReadFile(testPath)
-	if err != nil {
-		t.Logf("could not read file: %v\n", err)
-
 )
-	}
-
-	if string(contents) != tmplGold {
+	"os"
-		t.Logf("contents did not match:\n\tExpected: %s\n\tGot: %s", tmplGold, string(contents))
-
 )
-	}
+	"path/filepath"
 
 	// Check if directory was created
 	pkgPath := filepath.Join(destDir, "PKG")
-	if _, err := os.Lstat(pkgPath); err != nil {
-	"io/ioutil"
+)
 	"testing"
-
 )
-	}
+)
 
 	// Check for .tmplkeep
 	tmplKeep := filepath.Join(pkgPath, ".tmplkeep")
-	if _, err := os.Lstat(tmplKeep); err == nil {
-		t.Logf(".tmplkeep files should NOT be retained upon execution: %s\n", tmplKeep)
-
 )
-
 var (
-
+	assert.True(err != nil) // .tmplkeep file should NOT be retained
-	"os"
 
 	"os"
-import (
-		t.Logf("could not write file: %v\n", err)
 
-)
-
 var (
+package registry
+var (
 
-	if err := tmpl.Execute(destDir, true, false); err != nil {
-		t.Logf("could not execute template: %v\n", err)
 
-)
-
 var (
-
-	contents, err = ioutil.ReadFile(testPath)
-import (
 import (
+)
 	"io/ioutil"
-package registry
 
-)
-
 var (
-
-	if string(contents) != tmplNewGold {
+	"io/ioutil"
+var (
 	"os"
-)
-		t.FailNow()
-
 var (
+	"path/filepath"
 }
I schema/convert.go
diff --git a/schema/convert.go b/schema/convert.go
new file mode 100644
index 0000000000000000000000000000000000000000..03ff44c69d353c59789d1678cfd290de45b2da04
--- /dev/null
+++ b/schema/convert.go
@@ -0,0 +1,46 @@
+package schema
+
+import (
+	"fmt"
+
+	"gopkg.in/yaml.v3"
+)
+
+// Unmarshal YAML to map[string]any instead of map[any]any.
+func Unmarshal(in []byte, out any) error {
+	var res any
+
+	if err := yaml.Unmarshal(in, &res); err != nil {
+		return err
+	}
+	*out.(*any) = mapValue(res)
+
+	return nil
+}
+
+func mapSlice(in []any) []any {
+	res := make([]any, len(in))
+	for i, v := range in {
+		res[i] = mapValue(v)
+	}
+	return res
+}
+
+func mapMap(in map[any]any) map[string]any {
+	res := make(map[string]any)
+	for k, v := range in {
+		res[fmt.Sprintf("%v", k)] = mapValue(v)
+	}
+	return res
+}
+
+func mapValue(v any) any {
+	switch v := v.(type) {
+	case []any:
+		return mapSlice(v)
+	case map[any]any:
+		return mapMap(v)
+	default:
+		return v
+	}
+}
I schema/schema.go
diff --git a/schema/schema.go b/schema/schema.go
new file mode 100644
index 0000000000000000000000000000000000000000..7f925d9a56e69d85dc96643821da9a080b3bcd24
--- /dev/null
+++ b/schema/schema.go
@@ -0,0 +1,49 @@
+package schema
+
+import (
+	_ "embed"
+	"fmt"
+	"io"
+	"strings"
+
+	"github.com/xeipuuv/gojsonschema"
+)
+
+var (
+	//go:embed tmpl.json
+	schema       []byte
+	schemaLoader = gojsonschema.NewBytesLoader(schema)
+)
+
+// Lint is for linting a recipe against the schema
+func Lint(r io.Reader) error {
+	data, err := io.ReadAll(r)
+	if err != nil {
+		return err
+	}
+	var m any
+	if err := Unmarshal(data, &m); err != nil {
+		return err
+	}
+	sourceLoader := gojsonschema.NewGoLoader(m)
+	result, err := gojsonschema.Validate(schemaLoader, sourceLoader)
+	if err != nil {
+		return err
+	}
+	if len(result.Errors()) > 0 {
+		return ResultErrors(result.Errors())
+	}
+	return nil
+}
+
+// ResultErrors is a slice of gojsonschema.ResultError that implements error
+type ResultErrors []gojsonschema.ResultError
+
+// Error implements error
+func (r ResultErrors) Error() string {
+	errs := make([]string, 0, len(r))
+	for _, re := range r {
+		errs = append(errs, fmt.Sprintf("%s: %s", re.Field(), re.Description()))
+	}
+	return strings.Join(errs, " | ")
+}
I schema/tmpl.json
diff --git a/schema/tmpl.json b/schema/tmpl.json
new file mode 100644
index 0000000000000000000000000000000000000000..b8acf9826e47b572262112985237ebeeb86d3797
--- /dev/null
+++ b/schema/tmpl.json
@@ -0,0 +1,51 @@
+{
+  "$schema": "https://json-schema.org/draft/2020-12/schema",
+  "$id": "https://git.jojodev.com/jolheiser/tmpl/src/branch/main/schema/tmpl.json",
+  "title": "tmpl template",
+  "description": "A template for tmpl",
+  "type": "object",
+  "required": [
+    "prompts"
+  ],
+  "additionalProperties": false,
+  "properties": {
+    "prompts": {
+      "description": "Template prompts",
+      "type": "array",
+      "minItems": 1,
+      "items": {
+        "type": "object",
+        "required": [
+          "id"
+        ],
+        "additionalProperties": false,
+        "properties": {
+          "id": {
+            "description": "The unique prompt ID",
+            "type": "string"
+          },
+          "label": {
+            "description": "A label to show instead of the ID when prompting",
+            "type": "string"
+          },
+          "default": {
+            "description": "A default value for the prompt",
+            "type": "string"
+          },
+          "help": {
+            "description": "A help message for more information on a prompt",
+            "type": "string"
+          },
+          "depends_on": {
+            "description": "A list of prompt IDs that this prompt depends on",
+            "type": "array",
+            "minItems": 1,
+            "items": {
+              "type": "string"
+            }
+          }
+        }
+      }
+    }
+  }
+}