[object Object]

← back to Cli Printing Press

refactor(cli): split generator render stages (#313)

79b134aa36c72160c6b28c23f016cb15fef112d2 · 2026-04-26 14:51:40 -0700 · Trevin Chow

Files touched

Diff

commit 79b134aa36c72160c6b28c23f016cb15fef112d2
Author: Trevin Chow <trevin@trevinchow.com>
Date:   Sun Apr 26 14:51:40 2026 -0700

    refactor(cli): split generator render stages (#313)
---
 .gitignore                      |  1 +
 internal/generator/generator.go | 66 ++++++++++++++++++++++++++++++++++++++---
 2 files changed, 63 insertions(+), 4 deletions(-)

diff --git a/.gitignore b/.gitignore
index aec884e0..21f7232f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
 .cache/
 .gotmp/
 .claude/worktrees/
+/refactor/artifacts/
 /printing-press
 library/
 dist/
diff --git a/internal/generator/generator.go b/internal/generator/generator.go
index 63d64567..f62c7aba 100644
--- a/internal/generator/generator.go
+++ b/internal/generator/generator.go
@@ -880,7 +880,7 @@ func archetypePlaybook(arch profiler.DomainArchetype) []PlaybookEntry {
 	}
 }
 
-func (g *Generator) Generate() error {
+func (g *Generator) prepareOutput() error {
 	dirs := []string{
 		filepath.Join("cmd", naming.CLI(g.Spec.Name)),
 		filepath.Join("internal", "cli"),
@@ -924,7 +924,10 @@ func (g *Generator) Generate() error {
 	// AsyncJobs detection so async endpoints reserve the wait identifiers.
 	g.dedupeFlagIdentifiers()
 
-	// Generate single files
+	return nil
+}
+
+func (g *Generator) renderSingleFiles() error {
 	singleFiles := map[string]string{
 		"main.go.tmpl":           filepath.Join("cmd", naming.CLI(g.Spec.Name), "main.go"),
 		"helpers.go.tmpl":        filepath.Join("internal", "cli", "helpers.go"),
@@ -982,6 +985,10 @@ func (g *Generator) Generate() error {
 		}
 	}
 
+	return nil
+}
+
+func (g *Generator) renderOptionalSupportFiles() error {
 	if g.Spec.HasHTMLExtraction() {
 		if err := g.renderTemplate("html_extract.go.tmpl", filepath.Join("internal", "cli", "html_extract.go"), g.Spec); err != nil {
 			return fmt.Errorf("rendering HTML extraction helper: %w", err)
@@ -1055,9 +1062,38 @@ func (g *Generator) Generate() error {
 		}
 	}
 
+	return nil
+}
+
+func (g *Generator) Generate() error {
+	if err := g.prepareOutput(); err != nil {
+		return err
+	}
+	if err := g.renderSingleFiles(); err != nil {
+		return err
+	}
+	if err := g.renderOptionalSupportFiles(); err != nil {
+		return err
+	}
+
+	promotedCommands, promotedResourceNames, promotedEndpointNames := buildPromotedCommandPlan(g.Spec)
+
+	if err := g.renderResourceCommands(promotedResourceNames, promotedEndpointNames); err != nil {
+		return err
+	}
+
+	if err := g.renderAuthFiles(); err != nil {
+		return err
+	}
+	if err := g.renderMCPEntrypoint(); err != nil {
+		return err
+	}
+	return g.renderVisionAndRootFiles(promotedCommands, promotedResourceNames)
+}
+
+func buildPromotedCommandPlan(apiSpec *spec.APISpec) ([]PromotedCommand, map[string]bool, map[string]string) {
 	// Compute promoted commands early — needed to determine Hidden flag on parent commands
-	promotedCommands := buildPromotedCommands(g.Spec)
-	hasPromoted := len(promotedCommands) > 0
+	promotedCommands := buildPromotedCommands(apiSpec)
 
 	// Build set of resource names that have promoted commands. Promoted commands
 	// replace the resource parent entirely — the promoted command wires sibling
@@ -1072,6 +1108,10 @@ func (g *Generator) Generate() error {
 		promotedEndpointNames[pc.ResourceName] = pc.EndpointName
 	}
 
+	return promotedCommands, promotedResourceNames, promotedEndpointNames
+}
+
+func (g *Generator) renderResourceCommands(promotedResourceNames map[string]bool, promotedEndpointNames map[string]string) error {
 	// Generate per-resource parent files + per-endpoint command files
 	// This produces more files (one per endpoint) which improves Breadth scoring
 	for name, resource := range g.Spec.Resources {
@@ -1194,6 +1234,10 @@ func (g *Generator) Generate() error {
 		}
 	}
 
+	return nil
+}
+
+func (g *Generator) renderAuthFiles() error {
 	// Always render auth command - use full OAuth2 template when authorization URL is present,
 	// browser cookie template for cookie-auth APIs, otherwise simple token-management template
 	authPath := filepath.Join("internal", "cli", "auth.go")
@@ -1225,6 +1269,10 @@ func (g *Generator) Generate() error {
 		}
 	}
 
+	return nil
+}
+
+func (g *Generator) renderMCPEntrypoint() error {
 	// MCP server: generate cmd/{name}-pp-mcp/ entry point and internal/mcp/ package
 	if g.VisionSet.MCP || true { // Always generate MCP for now
 		mcpDirs := []string{
@@ -1241,6 +1289,12 @@ func (g *Generator) Generate() error {
 		}
 	}
 
+	return nil
+}
+
+func (g *Generator) renderVisionAndRootFiles(promotedCommands []PromotedCommand, promotedResourceNames map[string]bool) error {
+	hasPromoted := len(promotedCommands) > 0
+
 	// Vision features: profile already computed in early profiling above
 	schema := BuildSchema(g.Spec)
 
@@ -1496,6 +1550,10 @@ func (g *Generator) Generate() error {
 		}
 	}
 
+	return g.renderRootProjectFiles(promotedCommands, promotedResourceNames, renderedWorkflowConstructors, renderedInsightConstructors)
+}
+
+func (g *Generator) renderRootProjectFiles(promotedCommands []PromotedCommand, promotedResourceNames map[string]bool, renderedWorkflowConstructors, renderedInsightConstructors []string) error {
 	// Root --help Long surfaces ALL verified-built novel features — the
 	// whole point of this change is to stop making agents do discovery
 	// for novel capabilities. A count cap (earlier draft used 3) neuters

← b8bb5c1e refactor(cli): share generated auth helpers (#312)  ·  back to Cli Printing Press  ·  refactor(cli): split generator vision render stages (#314) 52a3dbc5 →