← back to Cli Printing Press
feat(templates): Go templates for all generated CLI files
a78f09790928b24c0925fe9330cf0c7d3f88ad79 · 2026-03-23 09:06:35 -0700 · Matt Van Horn
Templates for: main.go, root.go, helpers.go, doctor.go, config.go,
client.go, command.go (per-resource), types.go, go.mod, goreleaser,
golangci, Makefile, README. Based on Steinberger patterns from
wacli/discrawl/sag source code analysis.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Files touched
A internal/templates/client.go.tmplA internal/templates/command.go.tmplA internal/templates/config.go.tmplA internal/templates/doctor.go.tmplA internal/templates/go.mod.tmplA internal/templates/golangci.yml.tmplA internal/templates/goreleaser.yaml.tmplA internal/templates/helpers.go.tmplA internal/templates/main.go.tmplA internal/templates/makefile.tmplA internal/templates/readme.md.tmplA internal/templates/root.go.tmplA internal/templates/types.go.tmpl
Diff
commit a78f09790928b24c0925fe9330cf0c7d3f88ad79
Author: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Date: Mon Mar 23 09:06:35 2026 -0700
feat(templates): Go templates for all generated CLI files
Templates for: main.go, root.go, helpers.go, doctor.go, config.go,
client.go, command.go (per-resource), types.go, go.mod, goreleaser,
golangci, Makefile, README. Based on Steinberger patterns from
wacli/discrawl/sag source code analysis.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---
internal/templates/client.go.tmpl | 101 ++++++++++++++++++++++++++
internal/templates/command.go.tmpl | 125 ++++++++++++++++++++++++++++++++
internal/templates/config.go.tmpl | 87 ++++++++++++++++++++++
internal/templates/doctor.go.tmpl | 69 ++++++++++++++++++
internal/templates/go.mod.tmpl | 10 +++
internal/templates/golangci.yml.tmpl | 12 +++
internal/templates/goreleaser.yaml.tmpl | 27 +++++++
internal/templates/helpers.go.tmpl | 37 ++++++++++
internal/templates/main.go.tmpl | 15 ++++
internal/templates/makefile.tmpl | 16 ++++
internal/templates/readme.md.tmpl | 44 +++++++++++
internal/templates/root.go.tmpl | 108 +++++++++++++++++++++++++++
internal/templates/types.go.tmpl | 8 ++
13 files changed, 659 insertions(+)
diff --git a/internal/templates/client.go.tmpl b/internal/templates/client.go.tmpl
new file mode 100644
index 00000000..5a2391f7
--- /dev/null
+++ b/internal/templates/client.go.tmpl
@@ -0,0 +1,101 @@
+package client
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+ "strings"
+ "time"
+)
+
+type Client struct {
+ BaseURL string
+ AuthHeader string
+ HTTPClient *http.Client
+}
+
+func New(baseURL, authHeader string, timeout time.Duration) *Client {
+ return &Client{
+ BaseURL: strings.TrimRight(baseURL, "/"),
+ AuthHeader: authHeader,
+ HTTPClient: &http.Client{Timeout: timeout},
+ }
+}
+
+func (c *Client) Get(path string, params map[string]string) (json.RawMessage, error) {
+ return c.do("GET", path, params, nil)
+}
+
+func (c *Client) Post(path string, body any) (json.RawMessage, error) {
+ return c.do("POST", path, nil, body)
+}
+
+func (c *Client) Delete(path string) (json.RawMessage, error) {
+ return c.do("DELETE", path, nil, nil)
+}
+
+func (c *Client) Put(path string, body any) (json.RawMessage, error) {
+ return c.do("PUT", path, nil, body)
+}
+
+func (c *Client) do(method, path string, params map[string]string, body any) (json.RawMessage, error) {
+ url := c.BaseURL + path
+
+ var bodyReader io.Reader
+ if body != nil {
+ b, err := json.Marshal(body)
+ if err != nil {
+ return nil, fmt.Errorf("marshaling body: %w", err)
+ }
+ bodyReader = strings.NewReader(string(b))
+ }
+
+ req, err := http.NewRequest(method, url, bodyReader)
+ if err != nil {
+ return nil, fmt.Errorf("creating request: %w", err)
+ }
+
+ if c.AuthHeader != "" {
+ req.Header.Set("Authorization", c.AuthHeader)
+ }
+ if body != nil {
+ req.Header.Set("Content-Type", "application/json")
+ }
+ req.Header.Set("User-Agent", "{{.Name}}-cli/{{.Version}}")
+
+ if params != nil {
+ q := req.URL.Query()
+ for k, v := range params {
+ if v != "" {
+ q.Set(k, v)
+ }
+ }
+ req.URL.RawQuery = q.Encode()
+ }
+
+ resp, err := c.HTTPClient.Do(req)
+ if err != nil {
+ return nil, fmt.Errorf("%s %s: %w", method, path, err)
+ }
+ defer resp.Body.Close()
+
+ respBody, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return nil, fmt.Errorf("reading response: %w", err)
+ }
+
+ if resp.StatusCode >= 400 {
+ return nil, fmt.Errorf("%s %s returned HTTP %d: %s", method, path, resp.StatusCode, truncateBody(respBody))
+ }
+
+ return json.RawMessage(respBody), nil
+}
+
+func truncateBody(b []byte) string {
+ s := string(b)
+ if len(s) > 200 {
+ return s[:200] + "..."
+ }
+ return s
+}
diff --git a/internal/templates/command.go.tmpl b/internal/templates/command.go.tmpl
new file mode 100644
index 00000000..892be853
--- /dev/null
+++ b/internal/templates/command.go.tmpl
@@ -0,0 +1,125 @@
+package cli
+
+import (
+ "encoding/json"
+ "fmt"
+
+ "github.com/spf13/cobra"
+)
+
+func new{{title .ResourceName}}Cmd(flags *rootFlags) *cobra.Command {
+ cmd := &cobra.Command{
+ Use: "{{.ResourceName}}",
+ Short: "{{.Resource.Description}}",
+ }
+{{range $eName, $endpoint := .Resource.Endpoints}}
+ cmd.AddCommand(new{{title $.ResourceName}}{{title $eName}}Cmd(flags))
+{{- end}}
+ return cmd
+}
+{{range $eName, $endpoint := .Resource.Endpoints}}
+func new{{title $.ResourceName}}{{title $eName}}Cmd(flags *rootFlags) *cobra.Command {
+{{- range $endpoint.Params}}
+{{- if not .Positional}}
+ var flag{{camel .Name}} {{goType .Type}}
+{{- end}}
+{{- end}}
+{{- range $endpoint.Body}}
+ var body{{camel .Name}} {{goType .Type}}
+{{- end}}
+
+ cmd := &cobra.Command{
+ Use: "{{$eName}}{{positionalArgs $endpoint}}",
+ Short: "{{$endpoint.Description}}",
+ RunE: func(cmd *cobra.Command, args []string) error {
+ c, err := flags.newClient()
+ if err != nil {
+ return err
+ }
+
+ path := "{{$endpoint.Path}}"
+{{- range $i, $p := $endpoint.Params}}
+{{- if .Positional}}
+ if len(args) < {{add $i 1}} {
+ return usageErr(fmt.Errorf("{{.Name}} is required"))
+ }
+ path = replacePathParam(path, "{{.Name}}", args[{{$i}}])
+{{- end}}
+{{- end}}
+
+{{- if eq $endpoint.Method "GET"}}
+ params := map[string]string{}
+{{- range $endpoint.Params}}
+{{- if not .Positional}}
+ if flag{{camel .Name}} != {{zeroVal .Type}} {
+ params["{{.Name}}"] = fmt.Sprintf("%v", flag{{camel .Name}})
+ }
+{{- end}}
+{{- end}}
+ data, err := c.Get(path, params)
+{{- else if eq $endpoint.Method "POST"}}
+ body := map[string]any{}
+{{- range $endpoint.Body}}
+ if body{{camel .Name}} != {{zeroVal .Type}} {
+ body["{{.Name}}"] = body{{camel .Name}}
+ }
+{{- end}}
+ data, err := c.Post(path, body)
+{{- else if eq $endpoint.Method "DELETE"}}
+ data, err := c.Delete(path)
+{{- else if eq $endpoint.Method "PUT"}}
+ body := map[string]any{}
+{{- range $endpoint.Body}}
+ if body{{camel .Name}} != {{zeroVal .Type}} {
+ body["{{.Name}}"] = body{{camel .Name}}
+ }
+{{- end}}
+ data, err := c.Put(path, body)
+{{- end}}
+ if err != nil {
+ return apiErr(err)
+ }
+
+ if flags.asJSON {
+ return flags.printJSON(cmd, json.RawMessage(data))
+ }
+
+ fmt.Fprintln(cmd.OutOrStdout(), string(data))
+ return nil
+ },
+ }
+
+{{- range $endpoint.Params}}
+{{- if not .Positional}}
+ cmd.Flags().{{cobraFlagFunc .Type}}(&flag{{camel .Name}}, "{{.Name}}", {{defaultVal .}}, "{{.Description}}")
+{{- if .Required}}
+ _ = cmd.MarkFlagRequired("{{.Name}}")
+{{- end}}
+{{- end}}
+{{- end}}
+{{- range $endpoint.Body}}
+ cmd.Flags().{{cobraFlagFunc .Type}}(&body{{camel .Name}}, "{{.Name}}", {{defaultVal .}}, "{{.Description}}")
+{{- if .Required}}
+ _ = cmd.MarkFlagRequired("{{.Name}}")
+{{- end}}
+{{- end}}
+
+ return cmd
+}
+{{end}}
+func replacePathParam(path, name, value string) string {
+ return fmt.Sprintf("%s", path[:0]) + // prevent unused import
+ fmt.Sprintf("%s", replaceAll(path, "{"+name+"}", value))
+}
+
+var replaceAll = func(s, old, new string) string {
+ import_strings := "strings"
+ _ = import_strings
+ result := s
+ for i := 0; i < len(result); i++ {
+ if i+len(old) <= len(result) && result[i:i+len(old)] == old {
+ result = result[:i] + new + result[i+len(old):]
+ }
+ }
+ return result
+}
diff --git a/internal/templates/config.go.tmpl b/internal/templates/config.go.tmpl
new file mode 100644
index 00000000..e7166fb1
--- /dev/null
+++ b/internal/templates/config.go.tmpl
@@ -0,0 +1,87 @@
+package config
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+{{- if eq .Config.Format "toml"}}
+
+ "github.com/pelletier/go-toml/v2"
+{{- end}}
+)
+
+type Config struct {
+ BaseURL string `{{configTag .Config.Format}} :"base_url"`
+ AuthHeader string `{{configTag .Config.Format}} :"auth_header"`
+ AuthSource string `{{configTag .Config.Format}} :"-"`
+ Path string `{{configTag .Config.Format}} :"-"`
+{{- range .Auth.EnvVars}}
+ {{envVarField .}} string `{{configTag $.Config.Format}} :"{{snake .}}"`
+{{- end}}
+}
+
+func Load(configPath string) (*Config, error) {
+ cfg := &Config{
+ BaseURL: "{{.BaseURL}}",
+ }
+
+ // Resolve config path
+ path := configPath
+ if path == "" {
+ path = os.Getenv("{{upper .Name}}_CONFIG")
+ }
+ if path == "" {
+ home, _ := os.UserHomeDir()
+ path = filepath.Join(home, ".config", "{{.Name}}-cli", "config.{{.Config.Format}}")
+ }
+ cfg.Path = path
+
+ // Try to load config file
+ data, err := os.ReadFile(path)
+ if err == nil {
+{{- if eq .Config.Format "toml"}}
+ if err := toml.Unmarshal(data, cfg); err != nil {
+ return nil, fmt.Errorf("parsing config %s: %w", path, err)
+ }
+{{- end}}
+ }
+
+ // Env var overrides
+{{- range .Auth.EnvVars}}
+ if v := os.Getenv("{{.}}"); v != "" {
+ cfg.{{envVarField .}} = v
+ cfg.AuthSource = "env:{{.}}"
+ }
+{{- end}}
+
+ return cfg, nil
+}
+
+func (c *Config) AuthHeader() string {
+ if c.AuthHeader != "" {
+ return c.AuthHeader
+ }
+{{- if eq .Auth.Type "api_key"}}
+ format := "{{.Auth.Format}}"
+{{- range .Auth.EnvVars}}
+ format = strings.ReplaceAll(format, "{ {{- envVarPlaceholder . -}} }", c.{{envVarField .}})
+{{- end}}
+ if strings.Contains(format, "{") {
+ return "" // not all vars resolved
+ }
+ return format
+{{- else if eq .Auth.Type "bearer_token"}}
+ {{- if gt (len .Auth.EnvVars) 0}}
+ if c.{{envVarField (index .Auth.EnvVars 0)}} != "" {
+ return "Bearer " + c.{{envVarField (index .Auth.EnvVars 0)}}
+ }
+ {{- end}}
+ return ""
+{{- else}}
+ return ""
+{{- end}}
+}
+
+// Ensure strings import is used
+var _ = strings.ReplaceAll
diff --git a/internal/templates/doctor.go.tmpl b/internal/templates/doctor.go.tmpl
new file mode 100644
index 00000000..b52b5853
--- /dev/null
+++ b/internal/templates/doctor.go.tmpl
@@ -0,0 +1,69 @@
+package cli
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "time"
+
+ "github.com/USER/{{.Name}}-cli/internal/config"
+ "github.com/spf13/cobra"
+)
+
+func newDoctorCmd(flags *rootFlags) *cobra.Command {
+ return &cobra.Command{
+ Use: "doctor",
+ Short: "Check CLI health",
+ RunE: func(cmd *cobra.Command, args []string) error {
+ report := map[string]any{}
+
+ // Check config
+ cfg, err := config.Load(flags.configPath)
+ if err != nil {
+ report["config"] = fmt.Sprintf("error: %s", err)
+ } else {
+ report["config"] = "ok"
+ report["config_path"] = cfg.Path
+ report["base_url"] = cfg.BaseURL
+ }
+
+ // Check auth
+ if cfg != nil {
+ header := cfg.AuthHeader()
+ if header == "" {
+ report["auth"] = "not configured"
+ } else {
+ report["auth"] = "configured"
+ report["auth_source"] = cfg.AuthSource
+ }
+ }
+
+ // Check API connectivity
+ if cfg != nil && cfg.BaseURL != "" {
+ httpClient := &http.Client{Timeout: 5 * time.Second}
+ resp, err := httpClient.Get(cfg.BaseURL)
+ if err != nil {
+ report["api"] = fmt.Sprintf("unreachable: %s", err)
+ } else {
+ resp.Body.Close()
+ report["api"] = fmt.Sprintf("reachable (HTTP %d)", resp.StatusCode)
+ }
+ }
+
+ report["version"] = version
+
+ if flags.asJSON {
+ return flags.printJSON(cmd, report)
+ }
+
+ // Human-readable output
+ w := cmd.OutOrStdout()
+ for _, key := range []string{"config", "config_path", "base_url", "auth", "auth_source", "api", "version"} {
+ if v, ok := report[key]; ok {
+ fmt.Fprintf(w, "%s=%v\n", key, v)
+ }
+ }
+ return nil
+ },
+ }
+}
diff --git a/internal/templates/go.mod.tmpl b/internal/templates/go.mod.tmpl
new file mode 100644
index 00000000..16f02592
--- /dev/null
+++ b/internal/templates/go.mod.tmpl
@@ -0,0 +1,10 @@
+module github.com/USER/{{.Name}}-cli
+
+go 1.23
+
+require (
+ github.com/spf13/cobra v1.9.1
+{{- if eq .Config.Format "toml"}}
+ github.com/pelletier/go-toml/v2 v2.2.4
+{{- end}}
+)
diff --git a/internal/templates/golangci.yml.tmpl b/internal/templates/golangci.yml.tmpl
new file mode 100644
index 00000000..5397226d
--- /dev/null
+++ b/internal/templates/golangci.yml.tmpl
@@ -0,0 +1,12 @@
+linters:
+ enable:
+ - errorlint
+ - govet
+ - ineffassign
+ - staticcheck
+ - unused
+
+formatters:
+ enable:
+ - gofmt
+ - goimports
diff --git a/internal/templates/goreleaser.yaml.tmpl b/internal/templates/goreleaser.yaml.tmpl
new file mode 100644
index 00000000..e463f0aa
--- /dev/null
+++ b/internal/templates/goreleaser.yaml.tmpl
@@ -0,0 +1,27 @@
+version: 2
+project_name: {{.Name}}-cli
+changelog:
+ disable: true
+builds:
+ - id: {{.Name}}-cli
+ main: ./cmd/{{.Name}}-cli
+ binary: {{.Name}}-cli
+ env:
+ - CGO_ENABLED=0
+ ldflags:
+ - -s -w -X github.com/USER/{{.Name}}-cli/internal/cli.version={{"{{"}} .Version {{"}}"}}
+ targets:
+ - darwin_amd64
+ - darwin_arm64
+ - linux_amd64
+ - linux_arm64
+ - windows_amd64
+ - windows_arm64
+archives:
+ - formats: [tar.gz]
+ name_template: "{{"{{"}} .ProjectName {{"}}"}}_{{"{{"}} .Version {{"}}"}}_{{"{{"}} .Os {{"}}"}}_{{"{{"}} .Arch {{"}}"}}"
+ format_overrides:
+ - goos: windows
+ formats: [zip]
+checksum:
+ name_template: checksums.txt
diff --git a/internal/templates/helpers.go.tmpl b/internal/templates/helpers.go.tmpl
new file mode 100644
index 00000000..3c0ed8b6
--- /dev/null
+++ b/internal/templates/helpers.go.tmpl
@@ -0,0 +1,37 @@
+package cli
+
+import "errors"
+
+var As = errors.As
+
+type cliError struct {
+ code int
+ err error
+}
+
+func (e *cliError) Error() string { return e.err.Error() }
+func (e *cliError) Unwrap() error { return e.err }
+
+func usageErr(err error) error { return &cliError{code: 2, err: err} }
+func configErr(err error) error { return &cliError{code: 3, err: err} }
+func authErr(err error) error { return &cliError{code: 4, err: err} }
+func apiErr(err error) error { return &cliError{code: 5, err: err} }
+
+func truncate(s string, max int) string {
+ if len(s) <= max {
+ return s
+ }
+ if max <= 3 {
+ return s[:max]
+ }
+ return s[:max-3] + "..."
+}
+
+func firstNonEmpty(items ...string) string {
+ for _, s := range items {
+ if s != "" {
+ return s
+ }
+ }
+ return ""
+}
diff --git a/internal/templates/main.go.tmpl b/internal/templates/main.go.tmpl
new file mode 100644
index 00000000..b765938e
--- /dev/null
+++ b/internal/templates/main.go.tmpl
@@ -0,0 +1,15 @@
+package main
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/USER/{{.Name}}-cli/internal/cli"
+)
+
+func main() {
+ if err := cli.Execute(); err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(cli.ExitCode(err))
+ }
+}
diff --git a/internal/templates/makefile.tmpl b/internal/templates/makefile.tmpl
new file mode 100644
index 00000000..489c5de1
--- /dev/null
+++ b/internal/templates/makefile.tmpl
@@ -0,0 +1,16 @@
+.PHONY: build test lint install clean
+
+build:
+ go build -o bin/{{.Name}}-cli ./cmd/{{.Name}}-cli
+
+test:
+ go test ./...
+
+lint:
+ golangci-lint run
+
+install:
+ go install ./cmd/{{.Name}}-cli
+
+clean:
+ rm -rf bin/
diff --git a/internal/templates/readme.md.tmpl b/internal/templates/readme.md.tmpl
new file mode 100644
index 00000000..8094f59b
--- /dev/null
+++ b/internal/templates/readme.md.tmpl
@@ -0,0 +1,44 @@
+# {{.Name}}-cli
+
+{{.Description}}
+
+## Install
+
+```
+go install github.com/USER/{{.Name}}-cli/cmd/{{.Name}}-cli@latest
+```
+
+## Usage
+
+```
+{{.Name}}-cli --help
+{{.Name}}-cli --version
+{{.Name}}-cli doctor
+```
+
+### Commands
+{{range $name, $resource := .Resources}}
+#### {{$name}}
+
+{{$resource.Description}}
+{{range $eName, $endpoint := $resource.Endpoints}}
+- `{{$.Name}}-cli {{$name}} {{$eName}}` - {{$endpoint.Description}}
+{{- end}}
+{{end}}
+
+### Output Formats
+
+- Default: human-readable table
+- `--json`: JSON output
+- `--plain`: tab-separated text for piping
+
+### Configuration
+
+Config file: `{{.Config.Path}}`
+
+Environment variables:
+{{- range .Auth.EnvVars}}
+- `{{.}}`
+{{- end}}
+
+## Generated by CLI Printing Press
diff --git a/internal/templates/root.go.tmpl b/internal/templates/root.go.tmpl
new file mode 100644
index 00000000..83f41562
--- /dev/null
+++ b/internal/templates/root.go.tmpl
@@ -0,0 +1,108 @@
+package cli
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "text/tabwriter"
+ "time"
+
+ "github.com/USER/{{.Name}}-cli/internal/client"
+ "github.com/USER/{{.Name}}-cli/internal/config"
+ "github.com/spf13/cobra"
+)
+
+var version = "{{.Version}}"
+
+type rootFlags struct {
+ asJSON bool
+ plain bool
+ quiet bool
+ configPath string
+ timeout time.Duration
+}
+
+func Execute() error {
+ var flags rootFlags
+
+ rootCmd := &cobra.Command{
+ Use: "{{.Name}}-cli",
+ Short: "{{.Description}}",
+ SilenceUsage: true,
+ SilenceErrors: true,
+ Version: version,
+ }
+ rootCmd.SetVersionTemplate("{{.Name}}-cli {{"{{"}} .Version {{"}}"}}\n")
+
+ rootCmd.PersistentFlags().BoolVar(&flags.asJSON, "json", false, "Output as JSON")
+ rootCmd.PersistentFlags().BoolVar(&flags.plain, "plain", false, "Output as plain tab-separated text")
+ rootCmd.PersistentFlags().BoolVar(&flags.quiet, "quiet", false, "Bare output, one value per line")
+ rootCmd.PersistentFlags().StringVar(&flags.configPath, "config", "", "Config file path")
+ rootCmd.PersistentFlags().DurationVar(&flags.timeout, "timeout", 30*time.Second, "Request timeout")
+
+{{- range $name, $resource := .Resources}}
+ rootCmd.AddCommand(new{{title $name}}Cmd(&flags))
+{{- end}}
+ rootCmd.AddCommand(newDoctorCmd(&flags))
+ rootCmd.AddCommand(newVersionCliCmd())
+
+ return rootCmd.Execute()
+}
+
+func ExitCode(err error) int {
+ var codeErr *cliError
+ if As(err, &codeErr) {
+ return codeErr.code
+ }
+ return 1
+}
+
+func (f *rootFlags) newClient() (*client.Client, error) {
+ cfg, err := config.Load(f.configPath)
+ if err != nil {
+ return nil, configErr(err)
+ }
+ return client.New(cfg.BaseURL, cfg.AuthHeader(), f.timeout), nil
+}
+
+func (f *rootFlags) printJSON(w *cobra.Command, v any) error {
+ enc := json.NewEncoder(w.OutOrStdout())
+ enc.SetIndent("", " ")
+ return enc.Encode(v)
+}
+
+func (f *rootFlags) printTable(w *cobra.Command, headers []string, rows [][]string) error {
+ if f.asJSON {
+ return fmt.Errorf("use printJSON for JSON output")
+ }
+ tw := tabwriter.NewWriter(w.OutOrStdout(), 2, 4, 2, ' ', 0)
+ header := ""
+ for i, h := range headers {
+ if i > 0 {
+ header += "\t"
+ }
+ header += h
+ }
+ fmt.Fprintln(tw, header)
+ for _, row := range rows {
+ line := ""
+ for i, cell := range row {
+ if i > 0 {
+ line += "\t"
+ }
+ line += cell
+ }
+ fmt.Fprintln(tw, line)
+ }
+ return tw.Flush()
+}
+
+func newVersionCliCmd() *cobra.Command {
+ return &cobra.Command{
+ Use: "version",
+ Short: "Print version",
+ Run: func(cmd *cobra.Command, args []string) {
+ fmt.Printf("{{.Name}}-cli %s\n", version)
+ },
+ }
+}
diff --git a/internal/templates/types.go.tmpl b/internal/templates/types.go.tmpl
new file mode 100644
index 00000000..545f38c2
--- /dev/null
+++ b/internal/templates/types.go.tmpl
@@ -0,0 +1,8 @@
+package types
+{{range $name, $typeDef := .Types}}
+type {{$name}} struct {
+{{- range $typeDef.Fields}}
+ {{title .Name}} {{goType .Type}} `json:"{{.Name}}"`
+{{- end}}
+}
+{{end}}
← b4ab56ba feat(spec): YAML spec parser with validation and Stytch test
·
back to Cli Printing Press
·
feat(validate): quality gates + Clerk/Loops test specs + int 4b510ed7 →