diff options
Diffstat (limited to 'internal/showcase/showcase.go')
| -rw-r--r-- | internal/showcase/showcase.go | 311 |
1 files changed, 176 insertions, 135 deletions
diff --git a/internal/showcase/showcase.go b/internal/showcase/showcase.go index 01e0e2b..1c057f3 100644 --- a/internal/showcase/showcase.go +++ b/internal/showcase/showcase.go @@ -1,8 +1,8 @@ package showcase import ( - "context" - "encoding/json" + "context" + "encoding/json" "fmt" "os" "os/exec" @@ -51,7 +51,7 @@ func New(cfg *config.Config, workDir string) *Generator { return &Generator{ config: cfg, workDir: workDir, - aiTool: "claude", // default to claude + aiTool: "amp", // default to amp } } @@ -157,25 +157,25 @@ func (g *Generator) GenerateShowcase(repoFilter []string, forceRegenerate bool) // runCommandWithTimeout runs a command with a short timeout and returns trimmed stdout. // Stderr is included in the error message for easier debugging when GITSYNCER_DEBUG=1. func runCommandWithTimeout(name string, args ...string) (string, error) { - ctx, cancel := context.WithTimeout(context.Background(), 8*time.Second) - defer cancel() - cmd := exec.CommandContext(ctx, name, args...) - out, err := cmd.CombinedOutput() - if ctx.Err() == context.DeadlineExceeded { - return "", fmt.Errorf("command timed out") - } - if err != nil { - // include a snippet of output for debugging - msg := strings.TrimSpace(string(out)) - if len(msg) > 300 { - msg = msg[:300] + "..." - } - if msg != "" { - return "", fmt.Errorf("%v: %s", err, msg) - } - return "", err - } - return string(out), nil + ctx, cancel := context.WithTimeout(context.Background(), 8*time.Second) + defer cancel() + cmd := exec.CommandContext(ctx, name, args...) + out, err := cmd.CombinedOutput() + if ctx.Err() == context.DeadlineExceeded { + return "", fmt.Errorf("command timed out") + } + if err != nil { + // include a snippet of output for debugging + msg := strings.TrimSpace(string(out)) + if len(msg) > 300 { + msg = msg[:300] + "..." + } + if msg != "" { + return "", fmt.Errorf("%v: %s", err, msg) + } + return "", err + } + return string(out), nil } // getRepositories returns a list of repository directories in the work directory @@ -222,35 +222,48 @@ func (g *Generator) generateProjectSummary(repoName string, forceRegenerate bool } } - // Determine which AI tool to use (only if we need to run it) - // Prefer hexai if available when default tool is "claude" (aligns with release flow) - selectedTool := g.aiTool - if !haveCachedSummary { - switch g.aiTool { - case "claude", "claude-code", "": - // Try hexai -> claude -> aichat - if _, err := exec.LookPath("hexai"); err == nil { - selectedTool = "hexai" - } else if _, err := exec.LookPath("claude"); err == nil { - selectedTool = "claude" - } else if _, err := exec.LookPath("aichat"); err == nil { - selectedTool = "aichat" - } else { - // No AI tool available; fall back to README-based summary later - selectedTool = "" - } - case "hexai", "aichat": - if _, err := exec.LookPath(g.aiTool); err != nil { - // Requested tool missing; fall back to README-based summary later - selectedTool = "" - } else { - selectedTool = g.aiTool - } - default: - // Unsupported tool configured; fall back to README-based summary later - selectedTool = "" - } - } + // Determine which AI tool to use (only if we need to run it) + // Prefer amp if available when default tool is "" (aligns with release flow) + selectedTool := g.aiTool + if !haveCachedSummary { + switch g.aiTool { + case "amp", "": + // Try amp -> hexai -> claude -> aichat + if _, err := exec.LookPath("amp"); err == nil { + selectedTool = "amp" + } else if _, err := exec.LookPath("hexai"); err == nil { + selectedTool = "hexai" + } else if _, err := exec.LookPath("claude"); err == nil { + selectedTool = "claude" + } else if _, err := exec.LookPath("aichat"); err == nil { + selectedTool = "aichat" + } else { + // No AI tool available; fall back to README-based summary later + selectedTool = "" + } + case "claude", "claude-code": + // Try claude -> hexai -> aichat + if _, err := exec.LookPath("claude"); err == nil { + selectedTool = "claude" + } else if _, err := exec.LookPath("hexai"); err == nil { + selectedTool = "hexai" + } else if _, err := exec.LookPath("aichat"); err == nil { + selectedTool = "aichat" + } else { + selectedTool = "" + } + case "hexai", "aichat": + if _, err := exec.LookPath(g.aiTool); err != nil { + // Requested tool missing; fall back to README-based summary later + selectedTool = "" + } else { + selectedTool = g.aiTool + } + default: + // Unsupported tool configured; fall back to README-based summary later + selectedTool = "" + } + } // Change to repository directory originalDir, err := os.Getwd() @@ -273,50 +286,78 @@ func (g *Generator) generateProjectSummary(repoName string, forceRegenerate bool // Get the summary - either from cache or by running AI tool var summary string - if haveCachedSummary { - summary = cachedSummary - fmt.Printf("Using cached AI summary\n") - } else { - prompt := "Please provide a 1-2 paragraph summary of this project, explaining what it does, why it's useful, and how it's implemented. Focus on the key features and architecture. Be concise but informative." - - var cmd *exec.Cmd - - switch selectedTool { - case "claude": - fmt.Printf("Running Claude command:\n") - fmt.Printf(" claude --model sonnet \"%s\"\n", prompt) - cmd = exec.Command("claude", "--model", "sonnet", prompt) - case "hexai": - // Use README content as stdin and pass the prompt as argument - fmt.Printf("Running hexai command (stdin payload)\n") - // Find README file - readmeFiles := []string{ - "README.md", "readme.md", "Readme.md", - "README.MD", "README.txt", "readme.txt", - "README", "readme", - } - var readmeContent []byte - var readmeFound bool - for _, readmeFile := range readmeFiles { - content, err := os.ReadFile(readmeFile) - if err == nil { - readmeContent = content - readmeFound = true - fmt.Printf(" Using %s as input\n", readmeFile) - break - } - } - if readmeFound { - fmt.Printf(" echo <README content> | hexai \"%s\"\n", prompt) - cmd = exec.Command("hexai", prompt) - cmd.Stdin = strings.NewReader(string(readmeContent)) - } else { - // Will fall back below - cmd = nil - } - case "aichat": - // For aichat, we need to read README.md and pipe it to aichat - fmt.Printf("Running aichat command:\n") + if haveCachedSummary { + summary = cachedSummary + fmt.Printf("Using cached AI summary\n") + } else { + prompt := "Please provide a 1-2 paragraph summary of this project, explaining what it does, why it's useful, and how it's implemented. Focus on the key features and architecture. Be concise but informative." + + var cmd *exec.Cmd + + switch selectedTool { + case "amp": + // Use README content as stdin and pass the prompt as --execute argument + fmt.Printf("Running amp command (stdin payload)\n") + // Find README file + readmeFiles := []string{ + "README.md", "readme.md", "Readme.md", + "README.MD", "README.txt", "readme.txt", + "README", "readme", + } + var readmeContent []byte + var readmeFound bool + for _, readmeFile := range readmeFiles { + content, err := os.ReadFile(readmeFile) + if err == nil { + readmeContent = content + readmeFound = true + fmt.Printf(" Using %s as input\n", readmeFile) + break + } + } + if readmeFound { + fmt.Printf(" echo <README content> | amp --execute \"%s\"\n", prompt) + cmd = exec.Command("amp", "--execute", prompt) + cmd.Stdin = strings.NewReader(string(readmeContent)) + } else { + // Will fall back below + cmd = nil + } + case "claude": + fmt.Printf("Running Claude command:\n") + fmt.Printf(" claude --model sonnet \"%s\"\n", prompt) + cmd = exec.Command("claude", "--model", "sonnet", prompt) + case "hexai": + // Use README content as stdin and pass the prompt as argument + fmt.Printf("Running hexai command (stdin payload)\n") + // Find README file + readmeFiles := []string{ + "README.md", "readme.md", "Readme.md", + "README.MD", "README.txt", "readme.txt", + "README", "readme", + } + var readmeContent []byte + var readmeFound bool + for _, readmeFile := range readmeFiles { + content, err := os.ReadFile(readmeFile) + if err == nil { + readmeContent = content + readmeFound = true + fmt.Printf(" Using %s as input\n", readmeFile) + break + } + } + if readmeFound { + fmt.Printf(" echo <README content> | hexai \"%s\"\n", prompt) + cmd = exec.Command("hexai", prompt) + cmd.Stdin = strings.NewReader(string(readmeContent)) + } else { + // Will fall back below + cmd = nil + } + case "aichat": + // For aichat, we need to read README.md and pipe it to aichat + fmt.Printf("Running aichat command:\n") // Find README file readmeFiles := []string{ @@ -337,46 +378,46 @@ func (g *Generator) generateProjectSummary(repoName string, forceRegenerate bool } } - if readmeFound { - fmt.Printf(" echo <README content> | aichat \"%s\"\n", prompt) - cmd = exec.Command("aichat", prompt) - cmd.Stdin = strings.NewReader(string(readmeContent)) - } else { - // Will fall back below - cmd = nil - } - default: - // No/unsupported tool; will fall back below - cmd = nil - } - - if cmd != nil { - if output, err := cmd.Output(); err == nil { - summary = strings.TrimSpace(string(output)) - } - } - - // Fallback: create a minimal summary from README if AI unavailable/failed - if summary == "" { - readmeFiles := []string{ - "README.md", "readme.md", "Readme.md", - "README.MD", "README.txt", "readme.txt", - "README", "readme", - } - for _, readmeFile := range readmeFiles { - if content, err := os.ReadFile(readmeFile); err == nil { - parts := strings.Split(strings.TrimSpace(string(content)), "\n\n") - if len(parts) > 0 { - summary = strings.TrimSpace(parts[0]) - break - } - } - } - if summary == "" { - summary = fmt.Sprintf("%s: source code repository.", repoName) - } - } - } + if readmeFound { + fmt.Printf(" echo <README content> | aichat \"%s\"\n", prompt) + cmd = exec.Command("aichat", prompt) + cmd.Stdin = strings.NewReader(string(readmeContent)) + } else { + // Will fall back below + cmd = nil + } + default: + // No/unsupported tool; will fall back below + cmd = nil + } + + if cmd != nil { + if output, err := cmd.Output(); err == nil { + summary = strings.TrimSpace(string(output)) + } + } + + // Fallback: create a minimal summary from README if AI unavailable/failed + if summary == "" { + readmeFiles := []string{ + "README.md", "readme.md", "Readme.md", + "README.MD", "README.txt", "readme.txt", + "README", "readme", + } + for _, readmeFile := range readmeFiles { + if content, err := os.ReadFile(readmeFile); err == nil { + parts := strings.Split(strings.TrimSpace(string(content)), "\n\n") + if len(parts) > 0 { + summary = strings.TrimSpace(parts[0]) + break + } + } + } + if summary == "" { + summary = fmt.Sprintf("%s: source code repository.", repoName) + } + } + } // Build URLs codebergURL := "" |
