diff options
| -rw-r--r-- | internal/cmd/release.go | 10 | ||||
| -rw-r--r-- | internal/cmd/showcase.go | 4 | ||||
| -rw-r--r-- | internal/cmd/sync.go | 2 | ||||
| -rw-r--r-- | internal/release/release.go | 69 | ||||
| -rw-r--r-- | internal/showcase/showcase.go | 49 | ||||
| -rw-r--r-- | internal/version/version.go | 2 |
6 files changed, 65 insertions, 71 deletions
diff --git a/internal/cmd/release.go b/internal/cmd/release.go index 88ce86b..5216b17 100644 --- a/internal/cmd/release.go +++ b/internal/cmd/release.go @@ -19,8 +19,8 @@ var releaseCmd = &cobra.Command{ Use: "release", Short: "Manage releases across platforms", Long: `Check for version tags without releases and create them across -GitHub and Codeberg. Supports AI-generated release notes via amp (stdin pipeline), -with fallback to hexai or Claude.`, +GitHub and Codeberg. Supports AI-generated release notes via opencode (launch run with glm-5.1:cloud), +with fallback to hexai, claude, or amp.`, } var releaseCheckCmd = &cobra.Command{ @@ -74,8 +74,8 @@ If no repository is specified, processes all configured repositories.`, # Create for specific repository without AI gitsyncer release create myproject --no-ai-notes - # Use amp for AI release notes -gitsyncer release create --ai-tool amp`, + # Use opencode for AI release notes + gitsyncer release create --ai-tool opencode`, Run: func(cmd *cobra.Command, args []string) { flags := buildFlags() flags.CheckReleases = true @@ -109,5 +109,5 @@ func init() { releaseCreateCmd.Flags().BoolVar(&noAINotes, "no-ai-notes", false, "disable AI-generated release notes (AI notes are enabled by default)") releaseCreateCmd.Flags().BoolVar(&updateExisting, "update-existing", false, "update existing releases with new AI-generated notes") releaseCreateCmd.Flags().StringVar(&templatePath, "template", "", "custom template for release notes") - releaseCreateCmd.Flags().StringVar(&aiTool, "ai-tool", "amp", "AI tool to use for release notes (amp, claude, or hexai; amp is tried first if available)") + releaseCreateCmd.Flags().StringVar(&aiTool, "ai-tool", "opencode", "AI tool to use for release notes (opencode, hexai, claude, or amp; opencode is tried first if available)") } diff --git a/internal/cmd/showcase.go b/internal/cmd/showcase.go index 3217318..d6d8faf 100644 --- a/internal/cmd/showcase.go +++ b/internal/cmd/showcase.go @@ -22,7 +22,7 @@ var showcaseCmd = &cobra.Command{ Short: "Generate AI-powered project showcase", Long: `Generate a comprehensive showcase of all your projects using AI. This feature creates a formatted document with project summaries, statistics, -and code snippets. By default uses opencode (local Ollama), with fallback to amp, hexai, and claude.`, +and code snippets. By default uses opencode (via ollama launch with glm-5.1:cloud), with fallback to hexai, claude, and amp.`, Example: ` # Generate showcase with cached summaries gitsyncer showcase @@ -63,6 +63,6 @@ func init() { showcaseCmd.Flags().StringVarP(&outputPath, "output", "o", "", "custom output eath (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", "opencode", "AI tool for summaries: opencode, amp, hexai, claude, or claude-code (default tries opencode→amp→hexai→claude)") + showcaseCmd.Flags().StringVar(&showcaseAITool, "ai-tool", "opencode", "AI tool for summaries: opencode, hexai, claude, amp, or claude-code (default tries opencode→hexai→claude→amp)") showcaseCmd.Flags().StringVar(&showcaseRepo, "repo", "", "only generate showcase for a single repository") } diff --git a/internal/cmd/sync.go b/internal/cmd/sync.go index a4fff84..ea4cb8a 100644 --- a/internal/cmd/sync.go +++ b/internal/cmd/sync.go @@ -194,7 +194,7 @@ func init() { syncCmd.PersistentFlags().BoolVar(&noReleases, "no-releases", false, "skip release checking after sync") syncCmd.PersistentFlags().BoolVar(&autoCreate, "auto-create-releases", false, "automatically create releases without confirmation") syncCmd.PersistentFlags().BoolVar(&noAIReleaseNotes, "no-ai-release-notes", false, "disable AI-generated release notes (AI notes are enabled by default)") - syncCmd.PersistentFlags().StringVar(&syncAITool, "ai-tool", "opencode", "AI tool to use for release notes when auto-creating (opencode, amp, claude, or hexai; opencode is tried first if available)") + syncCmd.PersistentFlags().StringVar(&syncAITool, "ai-tool", "opencode", "AI tool to use for release notes when auto-creating (opencode, hexai, claude, or amp; opencode is tried first if available)") syncCmd.PersistentFlags().BoolVarP(&syncForce, "force", "f", false, "force sync even if normal sync interval checks would skip a repository") syncCmd.PersistentFlags().BoolVar(&throttle, "throttle", false, "throttle syncing based on local repo activity") } diff --git a/internal/release/release.go b/internal/release/release.go index 0b0bcdf..9ddf563 100644 --- a/internal/release/release.go +++ b/internal/release/release.go @@ -431,48 +431,25 @@ func (m *Manager) GenerateAIReleaseNotes(repoPath, repoName, tag string, allTags var releaseNotes string - // 0) Try opencode first (local Ollama with gpt-oss:120b) - if _, err := exec.LookPath("opencode"); err == nil { - fmt.Println(" Running opencode CLI command (stdin payload)...") - cmd := exec.Command("opencode", "run", "--model", "ollama/gpt-oss:120b", instr.String()) - cmd.Stdin = strings.NewReader(input.String()) + // 0) Try opencode first (glm-5.1:cloud via ollama launch) + if _, err := exec.LookPath("ollama"); err == nil { + fmt.Println(" Running ollama launch opencode ...") + cmd := exec.Command("ollama", "launch", "opencode", "--model", "glm-5.1:cloud", "-y", "--", "run", fullPrompt) cmd.Stderr = os.Stderr out, err := cmd.Output() if err != nil { - fmt.Printf(" opencode CLI failed: %v\n", err) + fmt.Printf("opencode ollama failed: %v\n", err) } else { notes := strings.TrimSpace(string(out)) if notes == "" { - fmt.Println(" opencode returned empty output; will try fallbacks...") + fmt.Println(" ollama opencode returned empty output; will try fallbacks...") } else { releaseNotes = notes } } } - // 1) Try amp as fallback: echo input to stdin and pass instructions as argument - // Note: print stderr to console, but only use stdout for notes - if releaseNotes == "" { - if _, err := exec.LookPath("amp"); err == nil { - fmt.Println(" Running amp CLI command (stdin payload)...") - cmd := exec.Command("amp", "--execute", instr.String()) - cmd.Stdin = strings.NewReader(input.String()) - cmd.Stderr = os.Stderr - out, err := cmd.Output() - if err != nil { - fmt.Printf(" amp CLI failed: %v\n", err) - } else { - notes := strings.TrimSpace(string(out)) - if notes == "" { - fmt.Println(" amp returned empty output; will try fallbacks...") - } else { - releaseNotes = notes - } - } - } - } - - // 2) Try hexai as fallback + // 1) Try hexai as fallback if releaseNotes == "" { if _, err := exec.LookPath("hexai"); err == nil { fmt.Println(" Running hexai CLI command (stdin payload)...") @@ -493,14 +470,12 @@ func (m *Manager) GenerateAIReleaseNotes(repoPath, repoName, tag string, allTags } } - if releaseNotes == "" && aiTool == "claude" { - fmt.Println(" Running claude CLI command...") - if _, err := exec.LookPath("claude"); err != nil { - fmt.Println(" claude CLI not found, all fallbacks exhausted") - } else { + // 2) Try claude as fallback + if releaseNotes == "" { + if _, err := exec.LookPath("claude"); err == nil { + fmt.Println(" Running claude CLI command...") cmd := exec.Command("claude", "--model", "sonnet", fullPrompt) cmd.Env = append(os.Environ(), "CLAUDE_DEBUG=1") - notes, err := m.executeAICommand(cmd, "claude") if err != nil { fmt.Printf(" Claude CLI failed: %v\n", err) @@ -510,8 +485,26 @@ func (m *Manager) GenerateAIReleaseNotes(repoPath, repoName, tag string, allTags } } - if releaseNotes == "" && (aiTool == "opencode" || aiTool == "amp") { - return "", fmt.Errorf("opencode/amp CLI not found in PATH and fallbacks failed") + // 3) Try amp as fallback: echo input to stdin and pass instructions as argument + // Note: print stderr to console, but only use stdout for notes + if releaseNotes == "" { + if _, err := exec.LookPath("amp"); err == nil { + fmt.Println(" Running amp CLI command (stdin payload)...") + cmd := exec.Command("amp", "--execute", instr.String()) + cmd.Stdin = strings.NewReader(input.String()) + cmd.Stderr = os.Stderr + out, err := cmd.Output() + if err != nil { + fmt.Printf(" amp CLI failed: %v\n", err) + } else { + notes := strings.TrimSpace(string(out)) + if notes == "" { + fmt.Println(" amp returned empty output; will try fallbacks...") + } else { + releaseNotes = notes + } + } + } } if releaseNotes == "" { diff --git a/internal/showcase/showcase.go b/internal/showcase/showcase.go index c6154f9..ebd48a7 100644 --- a/internal/showcase/showcase.go +++ b/internal/showcase/showcase.go @@ -51,7 +51,7 @@ func New(cfg *config.Config, workDir string) *Generator { return &Generator{ config: cfg, workDir: workDir, - aiTool: "opencode", // default to opencode (local Ollama with gpt-oss:120b) + aiTool: "opencode", // default to opencode (via ollama launch with glm-5.1:cloud) } } @@ -221,38 +221,39 @@ func findReadmeContent(repoPath string) ([]byte, string, bool) { func selectSummaryTool(aiTool string) string { switch aiTool { case "opencode", "": - // Default chain: opencode → amp → hexai → claude - if _, err := exec.LookPath("opencode"); err == nil { + // Default chain: opencode (via ollama launch) → hexai → claude → amp + if _, err := exec.LookPath("ollama"); err == nil { return "opencode" } - if _, err := exec.LookPath("amp"); err == nil { - return "amp" - } if _, err := exec.LookPath("hexai"); err == nil { return "hexai" } if _, err := exec.LookPath("claude"); err == nil { return "claude" } - case "amp": - // Explicit amp: amp → hexai → claude if _, err := exec.LookPath("amp"); err == nil { return "amp" } + case "hexai": + // Explicit hexai: hexai → claude → amp if _, err := exec.LookPath("hexai"); err == nil { return "hexai" } if _, err := exec.LookPath("claude"); err == nil { return "claude" } + if _, err := exec.LookPath("amp"); err == nil { + return "amp" + } case "claude", "claude-code": + // Explicit claude: claude → amp if _, err := exec.LookPath("claude"); err == nil { return "claude" } - if _, err := exec.LookPath("hexai"); err == nil { - return "hexai" + if _, err := exec.LookPath("amp"); err == nil { + return "amp" } - case "hexai": + case "amp": if _, err := exec.LookPath(aiTool); err == nil { return aiTool } @@ -266,31 +267,31 @@ func runSummaryTool(selectedTool, prompt, repoPath, readmeFile string, readmeCon switch selectedTool { case "opencode": - fmt.Printf("Running opencode command (stdin payload)\n") + fmt.Printf("Running ollama launch opencode command\n") if readmeFound { - fmt.Printf(" echo <README content> | opencode run --model ollama/gpt-oss:120b \"%s\"\n", prompt) + fullPrompt := prompt + "\n\nREADME content:\n" + string(readmeContent) + fmt.Printf(" ollama launch opencode --model glm-5.1:cloud -y -- run \"...\"\n") fmt.Printf(" Using %s as input\n", readmeFile) - cmd = exec.Command("opencode", "run", "--model", "ollama/gpt-oss:120b", prompt) - cmd.Stdin = strings.NewReader(string(readmeContent)) + cmd = exec.Command("ollama", "launch", "opencode", "--model", "glm-5.1:cloud", "-y", "--", "run", fullPrompt) } - case "amp": - fmt.Printf("Running amp command (stdin payload)\n") + case "hexai": + fmt.Printf("Running hexai command (stdin payload)\n") if readmeFound { - fmt.Printf(" echo <README content> | amp --execute \"%s\"\n", prompt) + fmt.Printf(" echo <README content> | hexai \"%s\"\n", prompt) fmt.Printf(" Using %s as input\n", readmeFile) - cmd = exec.Command("amp", "--execute", prompt) + cmd = exec.Command("hexai", prompt) cmd.Stdin = strings.NewReader(string(readmeContent)) } 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": - fmt.Printf("Running hexai command (stdin payload)\n") + case "amp": + fmt.Printf("Running amp command (stdin payload)\n") if readmeFound { - fmt.Printf(" echo <README content> | hexai \"%s\"\n", prompt) + fmt.Printf(" echo <README content> | amp --execute \"%s\"\n", prompt) fmt.Printf(" Using %s as input\n", readmeFile) - cmd = exec.Command("hexai", prompt) + cmd = exec.Command("amp", "--execute", prompt) cmd.Stdin = strings.NewReader(string(readmeContent)) } } @@ -788,7 +789,7 @@ func (g *Generator) generateProjectSummary(repoName string, forceRegenerate bool } // 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) + // Prefer opencode if available when default tool is "" (aligns with release flow) selectedTool := g.aiTool if !haveCachedSummary { selectedTool = selectSummaryTool(g.aiTool) diff --git a/internal/version/version.go b/internal/version/version.go index 189f928..2516192 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.17.0" + Version = "0.17.1" // GitCommit is the git commit hash at build time GitCommit = "unknown" |
