← back to Cli Printing Press
fix(generator): auth format, module path, and example values
0063ee7611abe10e8c219f1de00b724a53e86565 · 2026-03-26 12:41:03 -0700 · Matt Van Horn
- Auth: client.go.tmpl now respects Auth.Format (Bot/Bearer/Basic),
Auth.Header (custom header names), and Auth.In (header/query)
- Config: AuthHeader() applies format strings (e.g., "Bot {bot_token}")
- Module path: derive from git config github.user/user.name, sanitize
for Go module paths (lowercase, no spaces/special chars)
- Examples: exampleValue() produces UUIDs for IDs, emails for email
fields, dates for date fields (not "abc123" everywhere)
- Templates: replace hardcoded "USER" with {{.Owner}} in sync/search
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Files touched
M internal/generator/generator.goM internal/generator/templates/analytics.go.tmplM internal/generator/templates/client.go.tmplM internal/generator/templates/config.go.tmplM internal/generator/templates/search.go.tmplM internal/generator/templates/sync.go.tmpl
Diff
commit 0063ee7611abe10e8c219f1de00b724a53e86565
Author: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Date: Thu Mar 26 12:41:03 2026 -0700
fix(generator): auth format, module path, and example values
- Auth: client.go.tmpl now respects Auth.Format (Bot/Bearer/Basic),
Auth.Header (custom header names), and Auth.In (header/query)
- Config: AuthHeader() applies format strings (e.g., "Bot {bot_token}")
- Module path: derive from git config github.user/user.name, sanitize
for Go module paths (lowercase, no spaces/special chars)
- Examples: exampleValue() produces UUIDs for IDs, emails for email
fields, dates for date fields (not "abc123" everywhere)
- Templates: replace hardcoded "USER" with {{.Owner}} in sync/search
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---
internal/generator/generator.go | 79 +++++++++++++++++---------
internal/generator/templates/analytics.go.tmpl | 2 +-
internal/generator/templates/client.go.tmpl | 9 ++-
internal/generator/templates/config.go.tmpl | 50 +++++++++++++---
internal/generator/templates/search.go.tmpl | 2 +-
internal/generator/templates/sync.go.tmpl | 2 +-
6 files changed, 106 insertions(+), 38 deletions(-)
diff --git a/internal/generator/generator.go b/internal/generator/generator.go
index d44c094b..83b46793 100644
--- a/internal/generator/generator.go
+++ b/internal/generator/generator.go
@@ -4,6 +4,7 @@ import (
"embed"
"fmt"
"os"
+ "os/exec"
"path/filepath"
"strconv"
"strings"
@@ -27,10 +28,24 @@ type Generator struct {
}
func New(s *spec.APISpec, outputDir string) *Generator {
- // Default Owner to "USER" for backward compatibility
if s.Owner == "" {
- s.Owner = "USER"
+ if out, err := exec.Command("git", "config", "github.user").Output(); err == nil && len(out) > 0 {
+ s.Owner = strings.TrimSpace(string(out))
+ } else if out, err := exec.Command("git", "config", "user.name").Output(); err == nil && len(out) > 0 {
+ s.Owner = strings.TrimSpace(string(out))
+ } else {
+ s.Owner = "USER"
+ }
}
+ // Sanitize owner for Go module path: lowercase, no spaces/special chars
+ s.Owner = strings.ToLower(s.Owner)
+ s.Owner = strings.ReplaceAll(s.Owner, " ", "-")
+ s.Owner = strings.Map(func(r rune) rune {
+ if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '-' || r == '_' {
+ return r
+ }
+ return -1
+ }, s.Owner)
g := &Generator{Spec: s, OutputDir: outputDir}
g.funcs = template.FuncMap{
"title": strings.Title,
@@ -592,32 +607,42 @@ func oneline(s string) string {
return s
}
-func exampleValue(paramName, paramType string) string {
- name := strings.ToLower(paramName)
- switch {
- case strings.HasSuffix(name, "_id") || strings.HasSuffix(name, "id") || name == "id":
- return "abc123"
- case strings.Contains(name, "email"):
+func exampleValue(p spec.Param) string {
+ nameLower := strings.ToLower(p.Name)
+
+ if strings.HasSuffix(nameLower, "_id") || nameLower == "id" {
+ return "550e8400-e29b-41d4-a716-446655440000"
+ }
+ if strings.Contains(nameLower, "email") {
return "user@example.com"
- case strings.Contains(name, "name"):
- return "my-resource"
- case strings.Contains(name, "date") || strings.HasSuffix(name, "_at"):
- return "2026-01-01"
- case strings.Contains(name, "url") || strings.Contains(name, "link"):
- return "https://example.com"
- case strings.Contains(name, "status"):
- return "active"
- case strings.Contains(name, "limit") || strings.Contains(name, "count"):
- return "25"
- case strings.Contains(name, "page"):
- return "1"
- case paramType == "integer" || paramType == "int":
+ }
+ if strings.Contains(nameLower, "url") || strings.Contains(nameLower, "link") {
+ return "https://example.com/resource"
+ }
+ if strings.Contains(nameLower, "name") || strings.Contains(nameLower, "title") {
+ return "example-resource"
+ }
+ if strings.Contains(nameLower, "date") || p.Format == "date" {
+ return "2026-01-15"
+ }
+ if strings.Contains(nameLower, "time") || p.Format == "date-time" {
+ return "2026-01-15T09:00:00Z"
+ }
+ if strings.Contains(nameLower, "token") || strings.Contains(nameLower, "key") {
+ return "your-token-here"
+ }
+ if strings.Contains(nameLower, "limit") || strings.Contains(nameLower, "count") || strings.Contains(nameLower, "size") {
+ if p.Type == "integer" || p.Type == "int" {
+ return "50"
+ }
+ }
+ if p.Type == "boolean" || p.Type == "bool" {
+ return "true"
+ }
+ if p.Type == "integer" || p.Type == "int" || p.Type == "number" || p.Type == "float" {
return "42"
- case paramType == "boolean" || paramType == "bool":
- return ""
- default:
- return "value"
}
+ return "example-value"
}
func (g *Generator) exampleLine(commandPath, endpointName string, endpoint spec.Endpoint) string {
@@ -629,7 +654,7 @@ func (g *Generator) exampleLine(commandPath, endpointName string, endpoint spec.
// Add positional arg placeholders with realistic values
for _, p := range endpoint.Params {
if p.Positional {
- val := exampleValue(p.Name, p.Type)
+ val := exampleValue(p)
if val == "" {
val = "<" + p.Name + ">"
}
@@ -642,7 +667,7 @@ func (g *Generator) exampleLine(commandPath, endpointName string, endpoint spec.
case "POST", "PUT", "PATCH":
for _, p := range endpoint.Body {
if p.Required && p.Type == "string" {
- val := exampleValue(p.Name, p.Type)
+ val := exampleValue(p)
if val == "" {
val = "value"
}
diff --git a/internal/generator/templates/analytics.go.tmpl b/internal/generator/templates/analytics.go.tmpl
index 572a1c44..b8b21c74 100644
--- a/internal/generator/templates/analytics.go.tmpl
+++ b/internal/generator/templates/analytics.go.tmpl
@@ -10,7 +10,7 @@ import (
"path/filepath"
"sort"
- "github.com/USER/{{.Name}}-cli/internal/store"
+ "github.com/{{.Owner}}/{{.Name}}-cli/internal/store"
"github.com/spf13/cobra"
)
diff --git a/internal/generator/templates/client.go.tmpl b/internal/generator/templates/client.go.tmpl
index 249305b5..daf6cea5 100644
--- a/internal/generator/templates/client.go.tmpl
+++ b/internal/generator/templates/client.go.tmpl
@@ -147,7 +147,14 @@ func (c *Client) do(method, path string, params map[string]string, body any) (js
return nil, err
}
if authHeader != "" {
- req.Header.Set("Authorization", authHeader)
+{{- if and .Auth .Auth.In (eq .Auth.In "query")}}
+ // API key goes in query parameter
+ q := req.URL.Query()
+ q.Set("{{if .Auth.Header}}{{.Auth.Header}}{{else}}api_key{{end}}", authHeader)
+ req.URL.RawQuery = q.Encode()
+{{- else}}
+ req.Header.Set("{{if .Auth.Header}}{{.Auth.Header}}{{else}}Authorization{{end}}", authHeader)
+{{- end}}
}
if bodyBytes != nil {
req.Header.Set("Content-Type", "application/json")
diff --git a/internal/generator/templates/config.go.tmpl b/internal/generator/templates/config.go.tmpl
index 6f81ae4d..755e58dc 100644
--- a/internal/generator/templates/config.go.tmpl
+++ b/internal/generator/templates/config.go.tmpl
@@ -74,27 +74,50 @@ func (c *Config) AuthHeader() string {
return c.AuthHeaderVal
}
{{- if eq .Auth.Type "api_key"}}
+ {{- if gt (len .Auth.EnvVars) 0}}
+ token := c.{{envVarField (index .Auth.EnvVars 0)}}
+ if token == "" {
+ return ""
+ }
+ {{- end}}
{{- range .Auth.EnvVars}}
if c.{{envVarField .}} == "" {
return ""
}
{{- end}}
- format := "{{.Auth.Format}}"
-{{- range .Auth.EnvVars}}
- format = strings.ReplaceAll(format, "{ {{- envVarPlaceholder . -}} }", c.{{envVarField .}})
-{{- end}}
- if strings.Contains(format, "{") {
- return "" // not all vars resolved
+ {{- if .Auth.Format}}
+ replacements := map[string]string{
+ {{- range .Auth.EnvVars}}
+ "{{envVarPlaceholder .}}": c.{{envVarField .}},
+ "{{.}}": c.{{envVarField .}},
+ {{- end}}
}
- return format
+ return applyAuthFormat("{{.Auth.Format}}", replacements)
+ {{- else if gt (len .Auth.EnvVars) 0}}
+ return token
+ {{- else}}
+ return ""
+ {{- end}}
{{- else if eq .Auth.Type "bearer_token"}}
if c.AccessToken != "" {
c.AuthSource = "oauth2"
+ {{- if .Auth.Format}}
+ return applyAuthFormat("{{.Auth.Format}}", map[string]string{"access_token": c.AccessToken, "token": c.AccessToken})
+ {{- else}}
return "Bearer " + c.AccessToken
+ {{- end}}
}
{{- if gt (len .Auth.EnvVars) 0}}
if c.{{envVarField (index .Auth.EnvVars 0)}} != "" {
+ {{- if .Auth.Format}}
+ return applyAuthFormat("{{.Auth.Format}}", map[string]string{
+ "{{envVarPlaceholder (index .Auth.EnvVars 0)}}": c.{{envVarField (index .Auth.EnvVars 0)}},
+ "{{index .Auth.EnvVars 0}}": c.{{envVarField (index .Auth.EnvVars 0)}},
+ "token": c.{{envVarField (index .Auth.EnvVars 0)}},
+ })
+ {{- else}}
return "Bearer " + c.{{envVarField (index .Auth.EnvVars 0)}}
+ {{- end}}
}
{{- end}}
return ""
@@ -103,6 +126,19 @@ func (c *Config) AuthHeader() string {
{{- end}}
}
+func applyAuthFormat(format string, replacements map[string]string) string {
+ if format == "" {
+ return ""
+ }
+ for key, value := range replacements {
+ format = strings.ReplaceAll(format, "{"+key+"}", value)
+ }
+ if strings.Contains(format, "{") {
+ return ""
+ }
+ return format
+}
+
func (c *Config) SaveTokens(clientID, clientSecret, accessToken, refreshToken string, expiry time.Time) error {
c.ClientID = clientID
c.ClientSecret = clientSecret
diff --git a/internal/generator/templates/search.go.tmpl b/internal/generator/templates/search.go.tmpl
index 2726b822..b88bac93 100644
--- a/internal/generator/templates/search.go.tmpl
+++ b/internal/generator/templates/search.go.tmpl
@@ -9,7 +9,7 @@ import (
"os"
"path/filepath"
- "github.com/USER/{{.Name}}-cli/internal/store"
+ "github.com/{{.Owner}}/{{.Name}}-cli/internal/store"
"github.com/spf13/cobra"
)
diff --git a/internal/generator/templates/sync.go.tmpl b/internal/generator/templates/sync.go.tmpl
index 0749adce..c1480da9 100644
--- a/internal/generator/templates/sync.go.tmpl
+++ b/internal/generator/templates/sync.go.tmpl
@@ -9,7 +9,7 @@ import (
"os"
"path/filepath"
- "github.com/USER/{{.Name}}-cli/internal/store"
+ "github.com/{{.Owner}}/{{.Name}}-cli/internal/store"
"github.com/spf13/cobra"
)
← eb59816b feat(generator): wire BuildSchema to store/sync/search templ
·
back to Cli Printing Press
·
feat(skill): add Phase 4.6 hallucination audit and anti-gami 7bcacea3 →