From 5ca3c39da7854a753d8535465ec42bebfa3fcf8e Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sat, 19 Jul 2025 16:45:29 +0300 Subject: feat: add aichat support for showcase project descriptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add --ai-tool flag to showcase command (default: claude) - Support aichat as alternative to claude for generating project summaries - When using aichat, read README.md and pipe it as input - Update documentation and examples - Bump version to 0.8.1 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- internal/cli/showcase_handler.go | 5 +++ internal/cli/showcase_only_handler.go | 5 +++ internal/cmd/showcase.go | 12 +++++-- internal/showcase/showcase.go | 67 ++++++++++++++++++++++++++++------- internal/version/version.go | 2 +- 5 files changed, 75 insertions(+), 16 deletions(-) (limited to 'internal') diff --git a/internal/cli/showcase_handler.go b/internal/cli/showcase_handler.go index a3dfd26..929ce95 100644 --- a/internal/cli/showcase_handler.go +++ b/internal/cli/showcase_handler.go @@ -24,6 +24,11 @@ func HandleShowcase(cfg *config.Config, flags *Flags) int { // Create showcase generator generator := showcase.New(cfg, flags.WorkDir) + // Set AI tool if specified + if flags.AITool != "" { + generator.SetAITool(flags.AITool) + } + // Generate showcase with optional filter if err := generator.GenerateShowcase(repoFilter, flags.Force); err != nil { log.Printf("ERROR: Failed to generate showcase: %v\n", err) diff --git a/internal/cli/showcase_only_handler.go b/internal/cli/showcase_only_handler.go index ff2a82a..f777729 100644 --- a/internal/cli/showcase_only_handler.go +++ b/internal/cli/showcase_only_handler.go @@ -45,6 +45,11 @@ func HandleShowcaseOnly(cfg *config.Config, flags *Flags) int { fmt.Println("\nGenerating showcase for all repositories...") generator := showcase.New(cfg, flags.WorkDir) + // Set AI tool if specified + if flags.AITool != "" { + generator.SetAITool(flags.AITool) + } + // Pass empty filter to process all repos if err := generator.GenerateShowcase(nil, flags.Force); err != nil { log.Printf("ERROR: Failed to generate showcase: %v\n", err) diff --git a/internal/cmd/showcase.go b/internal/cmd/showcase.go index e184700..802cf10 100644 --- a/internal/cmd/showcase.go +++ b/internal/cmd/showcase.go @@ -13,14 +13,15 @@ var ( outputPath string outputFormat string excludePattern string + showcaseAITool string ) var showcaseCmd = &cobra.Command{ Use: "showcase", Short: "Generate AI-powered project showcase", - Long: `Generate a comprehensive showcase of all your projects using Claude AI. + Long: `Generate a comprehensive showcase of all your projects using AI. This feature creates a formatted document with project summaries, statistics, -and code snippets.`, +and code snippets. By default uses Claude, but can also use aichat.`, Example: ` # Generate showcase with cached summaries gitsyncer showcase @@ -34,11 +35,15 @@ and code snippets.`, gitsyncer showcase --format markdown # Exclude certain repositories - gitsyncer showcase --exclude "test-.*"`, + gitsyncer showcase --exclude "test-.*" + + # Use aichat instead of claude for AI summaries + gitsyncer showcase --ai-tool aichat`, Run: func(cmd *cobra.Command, args []string) { flags := buildFlags() flags.Showcase = true flags.Force = forceRegenerate + flags.AITool = showcaseAITool fmt.Println("Running showcase generation for all repositories...") exitCode := cli.HandleShowcaseOnly(cfg, flags) @@ -54,4 +59,5 @@ func init() { showcaseCmd.Flags().StringVarP(&outputPath, "output", "o", "", "custom output path (default: ~/git/foo.zone-content/gemtext/about/showcase.gmi.tpl)") showcaseCmd.Flags().StringVar(&outputFormat, "format", "gemtext", "output format: gemtext, markdown, html") showcaseCmd.Flags().StringVar(&excludePattern, "exclude", "", "exclude repos matching pattern") + showcaseCmd.Flags().StringVar(&showcaseAITool, "ai-tool", "claude", "AI tool to use for project summaries (claude or aichat)") } \ No newline at end of file diff --git a/internal/showcase/showcase.go b/internal/showcase/showcase.go index 8c49b54..815614c 100644 --- a/internal/showcase/showcase.go +++ b/internal/showcase/showcase.go @@ -17,6 +17,7 @@ import ( type Generator struct { config *config.Config workDir string + aiTool string } // ProjectSummary holds the summary information for a project @@ -49,9 +50,15 @@ func New(cfg *config.Config, workDir string) *Generator { return &Generator{ config: cfg, workDir: workDir, + aiTool: "claude", // default to claude } } +// SetAITool sets the AI tool to use for generating summaries +func (g *Generator) SetAITool(tool string) { + g.aiTool = tool +} + // GenerateShowcase generates a showcase for repositories // If repoFilter is provided, only those repositories are processed // If repoFilter is empty/nil, all repositories in work directory are processed @@ -184,16 +191,16 @@ func (g *Generator) generateProjectSummary(repoName string, forceRegenerate bool var haveCachedSummary bool if !forceRegenerate { if cached, err := g.loadFromCache(cacheFile); err == nil { - fmt.Printf("Using cached Claude summary (cache file: %s)\n", cacheFile) + fmt.Printf("Using cached AI summary (cache file: %s)\n", cacheFile) cachedSummary = cached.Summary haveCachedSummary = true } } - // Check if claude command exists (only if we need to run it) + // Check if AI tool command exists (only if we need to run it) if !haveCachedSummary { - if _, err := exec.LookPath("claude"); err != nil { - return nil, fmt.Errorf("claude command not found. Please install Claude CLI") + if _, err := exec.LookPath(g.aiTool); err != nil { + return nil, fmt.Errorf("%s command not found. Please install %s CLI", g.aiTool, g.aiTool) } } @@ -216,27 +223,63 @@ func (g *Generator) generateProjectSummary(repoName string, forceRegenerate bool // Continue anyway with partial metadata } - // Get the summary - either from cache or by running Claude + // Get the summary - either from cache or by running AI tool var summary string if haveCachedSummary { summary = cachedSummary - fmt.Printf("Using cached Claude summary\n") + fmt.Printf("Using cached AI summary\n") } else { - // Run claude command 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." - fmt.Printf("Running Claude command:\n") - fmt.Printf(" claude --model sonnet \"%s\"\n", prompt) + var cmd *exec.Cmd + + switch g.aiTool { + case "claude": + fmt.Printf("Running Claude command:\n") + fmt.Printf(" claude --model sonnet \"%s\"\n", prompt) + cmd = exec.Command("claude", "--model", "sonnet", prompt) + 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{ + "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 { + return nil, fmt.Errorf("no README file found for aichat input") + } + + fmt.Printf(" echo | aichat \"%s\"\n", prompt) + cmd = exec.Command("aichat", prompt) + cmd.Stdin = strings.NewReader(string(readmeContent)) + default: + return nil, fmt.Errorf("unsupported AI tool: %s", g.aiTool) + } - cmd := exec.Command("claude", "--model", "sonnet", prompt) output, err := cmd.Output() if err != nil { - return nil, fmt.Errorf("failed to run claude: %w", err) + return nil, fmt.Errorf("failed to run %s: %w", g.aiTool, err) } summary = strings.TrimSpace(string(output)) if summary == "" { - return nil, fmt.Errorf("received empty summary from claude") + return nil, fmt.Errorf("received empty summary from %s", g.aiTool) } } diff --git a/internal/version/version.go b/internal/version/version.go index b45fdc1..b31f9ec 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -7,7 +7,7 @@ import ( var ( // Version is the current version of gitsyncer - Version = "0.8.0" + Version = "0.8.1" // GitCommit is the git commit hash at build time GitCommit = "unknown" -- cgit v1.2.3