diff --git a/go.mod b/go.mod index 9c8063f..bd93d9e 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/agenticgokit/agk go 1.24.1 require ( - github.com/agenticgokit/agenticgokit v0.5.2 + github.com/agenticgokit/agenticgokit v0.5.3 github.com/charmbracelet/bubbles v0.21.0 github.com/charmbracelet/bubbletea v1.3.10 github.com/charmbracelet/lipgloss v1.1.0 diff --git a/go.sum b/go.sum index 6e07995..841f2a7 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/agenticgokit/agenticgokit v0.5.2 h1:RdzMQB7E2S8TMVgz6MOjiRMUyN1tGWXV7om/SYWpwMc= -github.com/agenticgokit/agenticgokit v0.5.2/go.mod h1:0EwU951CZIGYwEOLnC5hJbC9lhNvM85FhrL6NTTDIZo= +github.com/agenticgokit/agenticgokit v0.5.3 h1:k9/oSwxJbpCVQCDNPY9yWaXkiCNRsttn2DLQbwcQvXY= +github.com/agenticgokit/agenticgokit v0.5.3/go.mod h1:0EwU951CZIGYwEOLnC5hJbC9lhNvM85FhrL6NTTDIZo= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= diff --git a/pkg/scaffold/engine.go b/pkg/scaffold/engine.go deleted file mode 100644 index a584003..0000000 --- a/pkg/scaffold/engine.go +++ /dev/null @@ -1,98 +0,0 @@ -// Package scaffold provides project scaffolding and template generation functionality. -// This file contains the template Engine for rendering workflow files. -package scaffold - -import ( - "os" - "path/filepath" - "strings" - "unicode" - - "github.com/agenticgokit/agk/internal/config" -) - -// capitalize converts the first letter to uppercase -func capitalize(s string) string { - if len(s) == 0 { - return s - } - r := []rune(s) - r[0] = unicode.ToUpper(r[0]) - return string(r) -} - -// Engine handles template rendering for scaffolding -type Engine struct{} - -// NewEngine creates a new template engine -func NewEngine() *Engine { - return &Engine{} -} - -// RenderWorkflow generates the main workflow Go files -func (e *Engine) RenderWorkflow(projectPath string, cfg *config.ProjectConfig) error { - workflowDir := filepath.Join(projectPath, "workflow") - - // Prepare template data - packageName := strings.ToLower(cfg.Name) - packageName = strings.ReplaceAll(packageName, "-", "_") - workflowName := capitalize(packageName) - - data := TemplateData{ - ProjectName: cfg.Name, - WorkflowName: workflowName, - Description: cfg.Description, - LLMProvider: cfg.LLMProvider, - } - - // Generate workflow.go - workflowContent, err := RenderTemplate("templates/workflow/workflow.go.tmpl", data) - if err != nil { - return err - } - if err := os.WriteFile(filepath.Join(workflowDir, "workflow.go"), []byte(workflowContent), 0600); err != nil { - return err - } - - // Generate agents.go - agentsContent, err := RenderTemplate("templates/workflow/agents.go.tmpl", data) - if err != nil { - return err - } - if err := os.WriteFile(filepath.Join(workflowDir, "agents.go"), []byte(agentsContent), 0600); err != nil { - return err - } - - // Generate factory.go - factoryContent, err := RenderTemplate("templates/workflow/factory.go.tmpl", data) - if err != nil { - return err - } - if err := os.WriteFile(filepath.Join(workflowDir, "factory.go"), []byte(factoryContent), 0600); err != nil { - return err - } - - return nil -} - -// RenderREADME generates the README.md file -func (e *Engine) RenderREADME(projectPath string, cfg *config.ProjectConfig) error { - readmePath := filepath.Join(projectPath, "README.md") - - data := TemplateData{ - ProjectName: cfg.Name, - Description: cfg.Description, - LLMProvider: cfg.LLMProvider, - } - - content, err := RenderTemplate("templates/workflow/README.md.tmpl", data) - if err != nil { - return err - } - - if err := os.WriteFile(readmePath, []byte(content), 0600); err != nil { - return err - } - - return nil -} diff --git a/pkg/scaffold/generator.go b/pkg/scaffold/generator.go deleted file mode 100644 index 84d3194..0000000 --- a/pkg/scaffold/generator.go +++ /dev/null @@ -1,151 +0,0 @@ -package scaffold - -import ( - "context" - "fmt" - "os" - "path/filepath" - "strings" -) - -// Generator handles file and directory generation -type Generator struct{} - -// NewGenerator creates a new generator -func NewGenerator() *Generator { - return &Generator{} -} - -// GenerateStructure creates the directory structure for a new project -func (g *Generator) GenerateStructure(ctx context.Context, projectPath string) error { - dirs := []string{ - "workflow", - "agents", - "internal/config", - "internal/utils", - "pkg", - "test/fixtures", - "test/mocks", - "docs", - ".agk", - } - - for _, dir := range dirs { - path := filepath.Join(projectPath, dir) - if err := os.MkdirAll(path, 0750); err != nil { - return fmt.Errorf("failed to create directory %s: %w", path, err) - } - } - - return nil -} - -// GenerateMainGo creates the main.go entry point for the project -func (g *Generator) GenerateMainGo(projectPath, projectName string) error { - packageName := strings.ToLower(projectName) - packageName = strings.ReplaceAll(packageName, "-", "_") - _ = packageName // Use packageName to avoid unused variable - - content := `package main - -import ( - "context" - "log" - - "github.com/agenticgokit/agenticgokit/v1beta/core" - "github.com/example/` + projectName + `/workflow" -) - -func main() { - ctx := context.Background() - - // Create workflow factory - factory := workflow.NewFactory("openai", "gpt-4") - - // Create workflow - wf, err := factory.CreateWorkflow() - if err != nil { - log.Fatalf("Failed to create workflow: %v", err) - } - - // Execute workflow - result, err := wf.Execute(ctx, "Hello, AgenticGoKit!") - if err != nil { - log.Fatalf("Workflow execution failed: %v", err) - } - - log.Printf("Result: %s", result) -} -` - - filePath := filepath.Join(projectPath, "main.go") - return os.WriteFile(filePath, []byte(content), 0600) -} - -// GenerateGoMod creates a go.mod file for the project -func (g *Generator) GenerateGoMod(projectPath, projectName string) error { - // Convert project name to module path - modulePath := "github.com/" + projectName - - content := `module ` + modulePath + ` - -go 1.21 - -require ( - github.com/agenticgokit/agenticgokit/v1beta v0.9.0 - github.com/spf13/cobra v1.8.0 - github.com/spf13/viper v1.18.0 - github.com/rs/zerolog v1.33.0 - github.com/fatih/color v1.16.0 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/google/uuid v1.5.0 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect - github.com/inconshreveable/log15 v2.3.2-0.20221150144038-414c3106be10+incompatible // indirect - github.com/magiconair/properties v1.8.7 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/pelletier/go-toml/v2 v2.1.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/spf13/afero v1.11.0 // indirect - github.com/spf13/cast v1.6.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/subosito/gotenv v1.6.0 // indirect - go.uber.org/multierr v1.11.0 // indirect - golang.org/x/exp v0.0.0-20231127185646-65229373498e // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/text v0.14.0 // indirect - gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) -` - - filePath := filepath.Join(projectPath, "go.mod") - return os.WriteFile(filePath, []byte(content), 0600) -} - -// GenerateTestFixtures creates test files and fixtures -func (g *Generator) GenerateTestFixtures(projectPath string) error { - testMainContent := `package main - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestExample(t *testing.T) { - assert.True(t, true) -} -` - - testPath := filepath.Join(projectPath, "test/fixtures") - mainTestPath := filepath.Join(projectPath, "main_test.go") - - if err := os.WriteFile(mainTestPath, []byte(testMainContent), 0600); err != nil { - return fmt.Errorf("failed to create main_test.go: %w", err) - } - - _ = testPath // Reserved for future use - return nil -} diff --git a/pkg/scaffold/service.go b/pkg/scaffold/service.go index e25680f..1606433 100644 --- a/pkg/scaffold/service.go +++ b/pkg/scaffold/service.go @@ -4,8 +4,6 @@ package scaffold import ( "context" "fmt" - "os" - "path/filepath" "github.com/fatih/color" "github.com/rs/zerolog" @@ -28,18 +26,14 @@ type GenerateOptions struct { // Service handles project scaffolding and generation type Service struct { logger *zerolog.Logger - templateEngine *Engine configGenerator *config.Generator - generator *Generator } // NewService creates a new scaffold service func NewService(logger *zerolog.Logger) *Service { return &Service{ logger: logger, - templateEngine: NewEngine(), configGenerator: config.NewGenerator(), - generator: NewGenerator(), } } @@ -49,70 +43,39 @@ func (s *Service) GenerateProject(ctx context.Context, opts GenerateOptions) err s.logger.Info().Str("project", opts.ProjectName).Msg("starting project generation") } - // Collect user input if interactive - projectConfig := &config.ProjectConfig{ - Name: opts.ProjectName, - Description: opts.Description, - Template: opts.Template, - LLMProvider: opts.LLMProvider, - AgentType: opts.AgentType, - } - - if opts.Interactive { - var err error - projectConfig, err = s.collectUserInput(ctx, projectConfig) - if err != nil { - return fmt.Errorf("failed to collect user input: %w", err) + // Resolve template type + var templateType TemplateType + switch opts.Template { + case "quickstart": + templateType = TemplateQuickstart + case "single-agent": + templateType = TemplateSingleAgent + case "multi-agent": + templateType = TemplateMultiAgent + case "mcp-tools": + templateType = TemplateMCPTools + case "workflow": + templateType = TemplateWorkflow + default: + // Default to single-agent if not specified or unknown + if opts.Template == "" { + templateType = TemplateSingleAgent + } else { + // Try to match string to type, otherwise error + templateType = TemplateType(opts.Template) } } - // Create project directory - fmt.Println(color.CyanString(" ✓ Creating directory structure")) - if err := os.MkdirAll(opts.ProjectPath, 0750); err != nil { - return fmt.Errorf("failed to create project directory: %w", err) - } - - // Generate directory structure - fmt.Println(color.CyanString(" ✓ Generating project structure")) - if err := s.generator.GenerateStructure(ctx, opts.ProjectPath); err != nil { - return fmt.Errorf("failed to generate project structure: %w", err) - } - - // Generate configuration file - fmt.Println(color.CyanString(" ✓ Generating agk.toml configuration")) - configPath := filepath.Join(opts.ProjectPath, "agk.toml") - if err := s.configGenerator.GenerateConfig(projectConfig, configPath); err != nil { - return fmt.Errorf("failed to generate configuration: %w", err) - } - - // Generate workflow files - fmt.Println(color.CyanString(" ✓ Creating workflow definitions")) - if err := s.templateEngine.RenderWorkflow(opts.ProjectPath, projectConfig); err != nil { - return fmt.Errorf("failed to generate workflow: %w", err) - } - - // Generate main.go - fmt.Println(color.CyanString(" ✓ Creating main.go entry point")) - if err := s.generator.GenerateMainGo(opts.ProjectPath, opts.ProjectName); err != nil { - return fmt.Errorf("failed to generate main.go: %w", err) - } - - // Generate README - fmt.Println(color.CyanString(" ✓ Generating README.md")) - if err := s.templateEngine.RenderREADME(opts.ProjectPath, projectConfig); err != nil { - return fmt.Errorf("failed to generate README: %w", err) - } - - // Generate go.mod - fmt.Println(color.CyanString(" ✓ Creating go.mod")) - if err := s.generator.GenerateGoMod(opts.ProjectPath, opts.ProjectName); err != nil { - return fmt.Errorf("failed to generate go.mod: %w", err) + // Get generator for template + generator, err := GetTemplateGenerator(templateType) + if err != nil { + return fmt.Errorf("failed to get template generator: %w", err) } - // Generate test fixtures - fmt.Println(color.CyanString(" ✓ Creating test fixtures")) - if err := s.generator.GenerateTestFixtures(opts.ProjectPath); err != nil { - return fmt.Errorf("failed to generate test fixtures: %w", err) + // Execute generation + fmt.Println(color.CyanString(" ✓ Generating %s project...", templateType)) + if err := generator.Generate(ctx, opts); err != nil { + return fmt.Errorf("project generation failed: %w", err) } if s.logger != nil { diff --git a/pkg/scaffold/templates/mcp-tools/go.mod.tmpl b/pkg/scaffold/templates/mcp-tools/go.mod.tmpl index 8efb679..b32187c 100644 --- a/pkg/scaffold/templates/mcp-tools/go.mod.tmpl +++ b/pkg/scaffold/templates/mcp-tools/go.mod.tmpl @@ -2,4 +2,4 @@ module {{.ProjectName}} go 1.24.1 -require github.com/agenticgokit/agenticgokit v0.5.2 +require github.com/agenticgokit/agenticgokit v0.5.3 diff --git a/pkg/scaffold/templates/quickstart/go.mod.tmpl b/pkg/scaffold/templates/quickstart/go.mod.tmpl index 48ae4b3..b5475c9 100644 --- a/pkg/scaffold/templates/quickstart/go.mod.tmpl +++ b/pkg/scaffold/templates/quickstart/go.mod.tmpl @@ -3,5 +3,5 @@ module github.com/example/{{.ProjectName}} go 1.21 require ( - github.com/agenticgokit/agenticgokit v0.5.2 + github.com/agenticgokit/agenticgokit v0.5.3 ) diff --git a/pkg/scaffold/templates/single-agent/go.mod.tmpl b/pkg/scaffold/templates/single-agent/go.mod.tmpl index 48ae4b3..b5475c9 100644 --- a/pkg/scaffold/templates/single-agent/go.mod.tmpl +++ b/pkg/scaffold/templates/single-agent/go.mod.tmpl @@ -3,5 +3,5 @@ module github.com/example/{{.ProjectName}} go 1.21 require ( - github.com/agenticgokit/agenticgokit v0.5.2 + github.com/agenticgokit/agenticgokit v0.5.3 ) diff --git a/pkg/scaffold/templates/workflow/go.mod.tmpl b/pkg/scaffold/templates/workflow/go.mod.tmpl index 8efb679..b32187c 100644 --- a/pkg/scaffold/templates/workflow/go.mod.tmpl +++ b/pkg/scaffold/templates/workflow/go.mod.tmpl @@ -2,4 +2,4 @@ module {{.ProjectName}} go 1.24.1 -require github.com/agenticgokit/agenticgokit v0.5.2 +require github.com/agenticgokit/agenticgokit v0.5.3