diff options
| author | Paul Buetow <paul@buetow.org> | 2025-08-17 09:18:18 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-08-17 09:18:18 +0300 |
| commit | a8db7af2a094a16393f0060e628310d4161b154f (patch) | |
| tree | e1240767a26261bed8a0bcbbdb31850ad8b38d4e /internal/release | |
| parent | 9603960eeea6b06c5184850c2c2af7d257c77fdd (diff) | |
feat(release): hexai-first AI release notes; chore(version): bump to 0.8.8v0.8.8
- Prefer hexai stdin pipeline for release notes generation
- Fallback to Claude then aichat
- Update CLI help and README
- Adjust integration tests for current CLI
Diffstat (limited to 'internal/release')
| -rw-r--r-- | internal/release/release.go | 108 |
1 files changed, 65 insertions, 43 deletions
diff --git a/internal/release/release.go b/internal/release/release.go index 7181152..9e9b2e5 100644 --- a/internal/release/release.go +++ b/internal/release/release.go @@ -330,50 +330,72 @@ func (m *Manager) GenerateAIReleaseNotes(repoPath, repoName, tag string, allTags return "", fmt.Errorf("failed to get diff: %w", err) } - // Prepare the prompt for the AI - var prompt strings.Builder - prompt.WriteString(fmt.Sprintf("Generate professional release notes for %s version %s.\n\n", repoName, tag)) - - if prevTag != "" { - prompt.WriteString(fmt.Sprintf("Previous version: %s\n", prevTag)) - } - - prompt.WriteString("\nCommit messages:\n") - for _, commit := range commits { - prompt.WriteString(fmt.Sprintf("- %s\n", commit)) - } - - prompt.WriteString("\nCode changes:\n") - prompt.WriteString(diff) - prompt.WriteString("\n\nBased on the commits and code changes above, write professional release notes that:\n") - prompt.WriteString("1. Start with a brief overview of what this release accomplishes\n") - prompt.WriteString("2. Group changes into logical sections (Features, Improvements, Bug Fixes, etc.)\n") - prompt.WriteString("3. Explain WHY each change is useful to users, not just what changed\n") - prompt.WriteString("4. Use clear, non-technical language where possible\n") - prompt.WriteString("5. Highlight any breaking changes or migration steps\n") - prompt.WriteString("6. Keep it concise but informative\n") - prompt.WriteString("7. Format using Markdown\n") - prompt.WriteString("\nDo not include the version number in the title as it will be added automatically.") - - fmt.Printf(" Prompt: Generate release notes for %s %s\n", repoName, tag) - fmt.Printf(" Prompt includes: %d commits, %.1fKB of code changes\n", len(commits), float64(len(diff))/1024) - fmt.Printf(" Total prompt length: %d characters\n", len(prompt.String())) - - // Determine which AI tool to use (default to claude if not set) - aiTool := m.aiTool - if aiTool == "" { - aiTool = "claude" - } - - var releaseNotes string - - if aiTool == "claude" { - fmt.Println(" Running claude CLI command...") + // Prepare prompt/instructions and input payload + var instr strings.Builder + instr.WriteString(fmt.Sprintf("Generate professional release notes for %s version %s.\n", repoName, tag)) + if prevTag != "" { + instr.WriteString(fmt.Sprintf("Previous version: %s\n", prevTag)) + } + instr.WriteString("\nBased on the provided commits and code changes, write professional release notes that:\n") + instr.WriteString("1. Start with a brief overview of what this release accomplishes\n") + instr.WriteString("2. Group changes into logical sections (Features, Improvements, Bug Fixes, etc.)\n") + instr.WriteString("3. Explain WHY each change is useful to users, not just what changed\n") + instr.WriteString("4. Use clear, non-technical language where possible\n") + instr.WriteString("5. Highlight any breaking changes or migration steps\n") + instr.WriteString("6. Keep it concise but informative\n") + instr.WriteString("7. Format using Markdown\n") + instr.WriteString("\nDo not include the version number in the title as it will be added automatically.") + + var input strings.Builder + input.WriteString("Commit messages:\n") + for _, commit := range commits { + input.WriteString(fmt.Sprintf("- %s\n", commit)) + } + input.WriteString("\nCode changes:\n") + input.WriteString(diff) + + fmt.Printf(" Prompt: Generate release notes for %s %s\n", repoName, tag) + fmt.Printf(" Prompt includes: %d commits, %.1fKB of code changes\n", len(commits), float64(len(diff))/1024) + fmt.Printf(" Total prompt length: %d characters\n", len(instr.String())+len(input.String())) + + // Determine which AI tool to use (default to claude if not set) + aiTool := m.aiTool + if aiTool == "" { + aiTool = "claude" + } + + // Build a full prompt string for tools that read a single argument + fullPrompt := instr.String() + "\n\n" + input.String() + + var releaseNotes string + + // 1) Try hexai first: echo input to stdin and pass instructions as argument + // Note: print stderr to console, but only use stdout for notes + if _, err := exec.LookPath("hexai"); err == nil { + fmt.Println(" Running hexai CLI command (stdin payload)...") + cmd := exec.Command("hexai", instr.String()) + cmd.Stdin = strings.NewReader(input.String()) + cmd.Stderr = os.Stderr + out, err := cmd.Output() + if err != nil { + fmt.Printf(" hexai CLI failed: %v\n", err) + } else { + notes := strings.TrimSpace(string(out)) + if notes == "" { + fmt.Println(" hexai returned empty output; will try fallbacks...") + } else { + releaseNotes = notes + } + } + } + + if releaseNotes == "" && aiTool == "claude" { + fmt.Println(" Running claude CLI command...") if _, err := exec.LookPath("claude"); err != nil { fmt.Println(" claude CLI not found, falling back to aichat...") aiTool = "aichat" } else { - cmd := exec.Command("claude", "--model", "sonnet", prompt.String()) + cmd := exec.Command("claude", "--model", "sonnet", fullPrompt) cmd.Env = append(os.Environ(), "CLAUDE_DEBUG=1") notes, err := m.executeAICommand(cmd, "claude") @@ -387,13 +409,13 @@ func (m *Manager) GenerateAIReleaseNotes(repoPath, repoName, tag string, allTags } } - if aiTool == "aichat" { + if releaseNotes == "" && aiTool == "aichat" { fmt.Println(" Running aichat CLI command...") if _, err := exec.LookPath("aichat"); err != nil { return "", fmt.Errorf("aichat CLI not found in PATH and claude fallback failed") } - cmd := exec.Command("aichat", prompt.String()) + cmd := exec.Command("aichat", fullPrompt) notes, err := m.executeAICommand(cmd, "aichat") if err != nil { return "", fmt.Errorf("aichat CLI failed: %w", err) @@ -801,4 +823,4 @@ func (m *Manager) UpdateCodebergRelease(owner, repo, tag, releaseNotes string) e } return nil -}
\ No newline at end of file +} |
